Metaprogramming Example

bruno.desthuilliers at gmail.com bruno.desthuilliers at gmail.com
Thu Apr 17 07:12:46 EDT 2008


On 17 avr, 04:27, andrew cooke <and... at acooke.org> wrote:
> 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:

>>> a = "it's wrong"
>>> b = "it's wrong"
>>> a is b
False
>>> a == b
True
>>> c = 99999
>>> d = 99999
>>> c is d
False
>>> c == d
True
>>> 0 is False
False
>>> 0 == False
True
>>>

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



More information about the Python-list mailing list