Metaclasses vs. standard Python reflection?

Ian Bicking ianb at colorstudy.com
Fri May 2 02:09:27 EDT 2003


On Thu, 2003-05-01 at 23:53, Jeremy Fincher wrote:
> I've been looking for examples of metaclasses I can use for a talk I'm
> going to be giving next week, but I've been having trouble finding
> examples that solve problems that wouldn't just as easily be via
> Python's uber-reflective __getattr__/__setattr__ and inheritance. 
> Does anyone have a good example of a problem that's solved more
> "beautifully" with metaclasses than with Python's standard reflection
> facilities?

I use a metaclass in SQLObject (sqlobject.org), for a class like:

class Person(SQLObject):

    firstName = StringCol()
    lastName = StringCol()
    address = KeyCol(foreignKey='Address')

[note, this syntax is a recent change, and doesn't follow the
documentation]

The result is that firstName, lastName, and address are all changed into
properties that connect to a database to retrieve the appropriate
columns.  This happens in the metaclass, which is to say that it happens
during class instantiation.  You could do it with __getattr__ and
__setattr__, but I believe the result wouldn't be nearly as clean.

But you don't need metaclasses.  In an earlier unreleased version I was
doing it like:

class _Person(SQLObject):
    ...

Person = SQLBuild(_Person)

Which created a factory (instead of a class), and then fiddled with the
class quite a bit as well (creating the necessary methods, since I also
wasn't using properties at the time).  The factory function in place of
the class isn't necessary anymore due to the __new__ method.  But the
metaclass doesn't really change things otherwise, it just saves that one
line (Person = SQLBuild(_Person)) where I invoke the function that
fiddles with the class.  Instead that happens implicitly because
SQLObject has a __metaclass__ attribute.  Which is fine -- metaclasses
are just a different phrasing of the same idea.

In fact, I think they are much more complex than they need be.  I'd
rather have it implemented something like:

class SQLObject(object):

    def __newclass__(className, bases, vardict):
        # change vardict, perhaps...
        cls = object.__newclass__(className, bases, vardict)
        # do more stuff...
        return cls

It really does seem to just boil down to effecting the class
instantiation, and there's no reason there couldn't be an __initclass__
and __newclass__ methods that were analogs of __init__ and __new__ for
instances.  You do need change the class *at* instantiation instead of
after if you want to fiddle with __slots__, and perhaps there's some
other similar cases (though I can't think of any others).   

Maybe people have other novel uses of metaclasses that couldn't be done
with a builder (well, couldn't be done as well... I suspect there's not
actually anything that *couldn't* be done by invoking a builder in some
way, you can even kind of do __slots__, but it just doesn't look at
nice).

  Ian







More information about the Python-list mailing list