Hi,
Thanks for the help a couple of days ago. I completed what I was
doing and wrote a summary which I've posted athttp://acooke.org/cute/PythonMeta0.html
(it's kind of long to post here). I hope it might be useful to
someone else - it's complete code for a simple metaprogramming task
that uses metaclasses and descriptors.
I'd also appreciate further feedback if I've done anything stupid or
if there's some interesting approach I've missed that might work
better.
1/ Not about metaprogramming, but ORMs. You write:
"""
I could use an existing ORM package, but my experience with
them hasn't been that positive (in particular, I find SQL to be very
useful and want to use it more than is normally possible with standard
ORM).
"""
Makes me wonder if you really took a close-enough look at
SQLAlchemy... (please ignore this if you did)
2/ about your code:
if (self.ignore_identical is False or
new_value is not obj._auto_write_dict[self.name]):
Take care, 'is' is the identity test operator, not the equality test
operator. I think you *really* want the equality operator here. If you
don't understand why, here's a sample run that's worth a longer
explanation:
Also, the parens are not needed, and boolean tests don't need to be
that explicit (ie, you don't necessarily need to test a boolean
expression against True or False). And, last point, double-negations
can be harder to understand.
May I suggest:
if not (self.ignore_identical and new_value ==
obj._auto_write_dict[self.name]):
3/ code again:
attr = getattr(obj.__class__,
"%s%s" % (self.prefix, self.name), None)
obj._auto_write_dict[self.name] = attr(obj, new_value)
Three points here:
- build the setter name once and "cache" it. It'll make your code more
readable (and save a couple cycles)
def __init__(self, name, ignore_identical=True, prefix='_set_'):
self.name = name
self.ignore_identical = ignore_identical
self.prefix = prefix
self.settername = "%s%s" % (self.prefix, self.name)
then use self.settername in __set__
- retrieving the setter on the object (instead of it's class) would be
more pythonic, and way simpler to read.
- None is not callable, so you code will crash if the object doesn't
define a setter. You can either test the return value of getattr
against None, put a try/except around the call, or use an identity
function as default value for getattr (which is what I'd do here)
Fix:
setter = getattr(obj, self.settername, lambda x : x)
obj._auto_write_dict[self.name] = setter(new_value)
About the article itself, I'd say it makes a good introduction to a
common metaprogramming pattern, but you'd have to get the opinion of
someone that has no experience with these topics. Ho, and yes : one
day, you'll get why it's such a good thing to unite functions and
methods !-)
My 2 cents