By using this site, you agree to our updated Privacy Policy and our Terms of Use. Manage your Cookies Settings.
431,900 Members | 1,078 Online
Bytes IT Community
+ Ask a Question
Need help? Post your question and get tips & solutions from a community of 431,900 IT Pros & Developers. It's quick & easy.

Portrait of a "real life" __metaclass__

P: n/a
I guess this sort of falls under the "shameless plug" category, but
here it is: Recently I used a custom metaclass in a Python program
I've been working on, and I ended up doing a sort of write-up on it,
as an example of what a "real life" __metaclass__ might do for those
who may never have seen such a thing themselves.

http://markshroyer.com/blog/2007/11/...ass-windmills/

So what's the verdict? Incorrect? Missed the point completely?
Needs to get his head checked? I'd love to hear what
comp.lang.python has to (anthropomorphically) say about it.

--
Mark Shroyer
http://markshroyer.com/
Nov 10 '07 #1
Share this Question
Share on Google+
3 Replies


P: n/a
On Nov 9, 7:12 pm, Mark Shroyer <usenet-m...@markshroyer.comwrote:
I guess this sort of falls under the "shameless plug" category, but
here it is: Recently I used a custom metaclass in a Python program
I've been working on, and I ended up doing a sort of write-up on it,
as an example of what a "real life" __metaclass__ might do for those
who may never have seen such a thing themselves.

http://markshroyer.com/blog/2007/11/...ass-windmills/

So what's the verdict? Incorrect? Missed the point completely?
Needs to get his head checked? I'd love to hear what
comp.lang.python has to (anthropomorphically) say about it.
Kinda wordy. Let me see if I got the point:

- You already had a bunch of classes that did age matching on date
time objects.

- You were building a class that matched emails.

- You wanted to reuse the code for age matching to do email matching
(based on the message's age)

- So you wrote a metaclass that replaced the match() method with a
proxy that would either dispatch to the old match() method (if it was
a datetime object) or dispatch to the new match() method (which
matched based on the message's date.)

Sounds Java-y, if that's even a word. Too many classes, not enough
functions. You can tell you are doing Java in Python when you feel the
urge to give everything a name that is a noun, even if it is
completely based of a verb, such as "matcher". My opinion is that if
you want to talk about doing something in Python such as matching,
starting writing functions that match and forget the classes. Classes
are for real nouns, nouns that can do several distinct things.

What would I have done? I wouldn't have had an age matching class. I
would have had a function that, given the datetime and a range
specification, would return true or false. Then I would've written
another function for matching emails. Again, it takes a specification
and the email and returns true or false.

If I really wanted to pass around the specifications as objects, I
would do what the re module does: have one generic object for all the
different kinds of age matching possible, and one generic object for
all the email objects possible. These would be called,
"AgeMatchSpecification", etc... These are noun-y things. Here,
however, they are really a way of keeping your data organized so you
can tell that that particular dict over there is an
AgeMatchSpecification and that one is an EmailMatchSpecification. And
remember, the specifications don't do the matching--they merely tell
the match function what it is you wanted matched.

Now, part of the email match specification would probably include bits
of the date match specification, because you'd want to match the
various dates attached to an email. That's really not rocket science
though.

There wouldn't be any need to integrate the classes anymore if I did
it that way. Plus, I wouldn't have to remember a bunch of class names.
I'd just have to remember the various parameters to the match
specification for age matching and a different set of parameters for
the email matching.

Nov 10 '07 #2

P: n/a
On 2007-11-10, Jonathan Gardner <jg**************************@gmail.comwrote:
On Nov 9, 7:12 pm, Mark Shroyer <usenet-m...@markshroyer.comwrote:
>I guess this sort of falls under the "shameless plug" category, but
here it is: Recently I used a custom metaclass in a Python program
I've been working on, and I ended up doing a sort of write-up on it,
as an example of what a "real life" __metaclass__ might do for those
who may never have seen such a thing themselves.

http://markshroyer.com/blog/2007/11/...ass-windmills/

So what's the verdict? Incorrect? Missed the point completely?
Needs to get his head checked? I'd love to hear what
comp.lang.python has to (anthropomorphically) say about it.

Kinda wordy.
Yeah, my fingers sort of latch onto the keyboard sometimes and just
won't let go. Sorry about that ;)
Let me see if I got the point:

- You already had a bunch of classes that did age matching on date
time objects.

- You were building a class that matched emails.

- You wanted to reuse the code for age matching to do email matching
(based on the message's age)

- So you wrote a metaclass that replaced the match() method with a
proxy that would either dispatch to the old match() method (if it was
a datetime object) or dispatch to the new match() method (which
matched based on the message's date.)
Close, but not quite. The proxy *always* dispatches to
AgeSpec.match(), but if the result of that method is an AgeSpec
itself, then the proxy wraps the result back up in a Matcher, which
works out rather conveniently for the rest of the application.
Sounds Java-y, if that's even a word.
Yeah, you pretty much nailed my original background right there. On
the other hand, I've also done a lot of work in Perl and C, and
pride myself on striving not to resort to OO patterns where they
aren't really useful. So let me try to defend my reasoning, if it
is in fact defensible...
Too many classes, not enough functions. You can tell you are doing
Java in Python when you feel the urge to give everything a name
that is a noun, even if it is completely based of a verb, such as
"matcher". My opinion is that if you want to talk about doing
something in Python such as matching, starting writing functions
that match and forget the classes. Classes are for real nouns,
nouns that can do several distinct things.

What would I have done? I wouldn't have had an age matching class. I
would have had a function that, given the datetime and a range
specification, would return true or false. Then I would've written
another function for matching emails. Again, it takes a specification
and the email and returns true or false.
There isn't much difference between

match_calendar_month(2007, 11, message)

and

m = CalendarMonthMatcher(2007, 11)
m.match(message)

so of course you're right that, were that all I'm doing with these
matchers, it would be a waste to implement them as classes. But
take for example two of my app's mailbox actions -- these aren't
their real names, but for clarity let's call them ArchiveByMonth and
SaveAttachmentsByMonth. The former moves messages from previous
months into an archival mbox file ./archives/YYYY/MM.mbox
corresponding to each message's month, and the latter saves message
attachments into a directory ./attachments/YYYY/MM/. Each of these
actions would work by using either match_calendar_month() or
CalendarMonthMatcher().match() to perform its action on all messages
within a given month; then it iterates through previous months and
repeats until there are no more messages left to be processed.

In my object-oriented implementation, this iteration is performed by
calling m.previous() on the current matcher, much like the
simplified example in my write-up. Without taking the OO approach,
on the other hand, both types of actions would need to compute the
previous month themselves; sure that's not an entirely burdensome
task, but it really seems like the wrong place for that code to
reside. (And if you tackle this by writing another method to return
the requisite (year, month) tuple, and apply that method alongside
wherever match_calendar_month() is used... well, at that point
you're really just doing object-oriented code without the "class"
keyword.)

Furthermore, suppose I want to save attachments by week instead of
month: I could then hand the SaveAttachmentsByPeriod action a
WeekMatcher instead of a MonthMatcher, and the action, using the
matcher's common interface, does the job just as expected. (This is
an actual configuration file option in the application; the nice
thing about taking an OO approach to this app is that there's a very
straightforward mapping between the configuration file syntax and
the actual implementation.)

It could be that I'm still "thinking in Java," as you rather
accurately put it, but here the object-oriented approach seems
genuinely superior -- cleaner and, well, with better encapsulated
functionality, to use the buzzword.
If I really wanted to pass around the specifications as objects, I
would do what the re module does: have one generic object for all the
different kinds of age matching possible, and one generic object for
all the email objects possible. These would be called,
"AgeMatchSpecification", etc... These are noun-y things. Here,
however, they are really a way of keeping your data organized so you
can tell that that particular dict over there is an
AgeMatchSpecification and that one is an EmailMatchSpecification. And
remember, the specifications don't do the matching--they merely tell
the match function what it is you wanted matched.
Oddly enough, the re module was sort of my inspiration here:

my_regex = re.compile("abc")
my_regex.match("some string")

(Sure, re.compile() is a factory function that produces SRE_Pattern
instances rather than the name of an actual class, but it's still
used in much the same way.)
Now, part of the email match specification would probably include bits
of the date match specification, because you'd want to match the
various dates attached to an email. That's really not rocket science
though.

There wouldn't be any need to integrate the classes anymore if I did
it that way. Plus, I wouldn't have to remember a bunch of class names.
I'd just have to remember the various parameters to the match
specification for age matching and a different set of parameters for
the email matching.
You're sort of missing the bigger picture of this application,
although that's entirely not your fault as I never fully described
it to begin with. The essence of this project is that I have a
family of mailbox actions (delete, copy, archive to mailbox, archive
by time period, ...) and a family of email matching rules (match
read messages, match messages with attachments, match messages of a
certain size, match messages by date, ...) of which matching by date
is only one subtype -- but there are even many different ways to
match by date (match by number of days old, match by specific
calendar month, match by specific calendar month *or older*, match
by day of the week, ...); not to mention arbitrary Boolean
combinations of other matching rules (and, or, not).

My goal is to create a highly configurable and extensible app, in
which the user can mix and match different "action" and "matcher"
instances to the highest degree possible. And using class
definitions really facilitates that, to my Java-poisoned mind. For
example, if the user writes in the config file

actions = (
(
# Save attachments from read messages at least 10 days old
mailbox =(
path ='/path/to/maildir',
type ='maildir',
),
match =(
type =And,
p =(
type =MarkedRead,
state =True,
),
q =(
type =DaysOld,
days =10,
),
),
action =(
type =SaveAttachments,
destination ='/some/directory/',
),
),
)

(can you tell I've been working with Lighttpd lately?)

then my app can easily read in this dictionary and map the
user-specified actions directly into Matcher and Action instances;
and this without me having to write a bunch of code to process
boolean logic, matching types, action parameters, and so on into a
program flow that has a structure needlessly divergent from the
configuration file syntax. It also means that, should a user
augment the program with his own Matcher or Action implementation,
as I intend to make it easy to do, then those implementations can be
used straightaway without even touching the code for the
configuration file reader.

As for the decision to use a metaclass proxy to my AgeSpec classes,
I'm fully prepared to admit wrongdoing there. But I still believe
that an object-oriented design is the best approach to this problem,
at least considering my design goals. Or am I *still* missing the
point?

Thanks for your input!
Mark

--
Mark Shroyer
http://markshroyer.com/
Nov 10 '07 #3

P: n/a
On Sat, 10 Nov 2007 03:12:25 +0000, Mark Shroyer wrote:
I guess this sort of falls under the "shameless plug" category, but here
it is: Recently I used a custom metaclass in a Python program I've been
working on, and I ended up doing a sort of write-up on it, as an example
of what a "real life" __metaclass__ might do for those who may never
have seen such a thing themselves.

http://markshroyer.com/blog/2007/11/...ass-windmills/

So what's the verdict? Incorrect? Missed the point completely? Needs
to get his head checked? I'd love to hear what comp.lang.python has to
(anthropomorphically) say about it.
Without having read the whole thing (I just looked at the metaclass
itself): it looks like for every subclass of AgeSpec you want to create
an associated (wrapper of some sort) class as well. Perfectly reasonable
usage as far as I'm concerned.

The only suggestion I have is to avoid "helpfully" binding values in the
user's namespace. It might seem helpful for your intended use, but
automatically binding names on the user's behalf is one of those things
that can suddenly become decidedly unhelpful when a user wants to use the
code in a way other than originally intended.

Instead, make the wrapper class an attribute of the main class. For
example:

def __init__(cls, name, bases, dict):
"Initialize new AgeSpecMatcher subclass."
# Let type.__init__() do the heavy lifting...
super(MetaAgeSpec, cls).__init__(cls, name, bases, dict)
print "MetaAgeSpec.__init__(%s, ...)" % cls.__name__
try:
# We want to build wrapper classes for all AgeSpec
# subclasses, but not for AgeSpec itself
if issubclass(cls, AgeSpec):
cls.Matcher = cls.wrapper_class()
except NameError:
pass
Then, whereever you would have used:

MonthAgeSpecMatcher()

you'd use

MonthAgeSpec.Matcher()

instead. Namespaces are one honking great idea--use them. At the very
least, give the user an option not to bind the additional name.

Carl Banks
Nov 10 '07 #4

This discussion thread is closed

Replies have been disabled for this discussion.