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