Using metaclasses to play with decorators.

Robert Brewer fumanchu at amor.org
Fri Jun 18 05:23:43 CEST 2004


j_mckitrick wrote:
> You guys are WAY over my head, and I'm supposed to be a C.S. 
> junior!  :-\
> 
> Could someone give me an idea of what metaclasses and 
> decorators _are_?

Sure thing.

> I think metaclasses are classes with their base methods 
> overridden, correct?
> 
> And decorators are a form of delegation, right?

Oh, and you were doing so well until you guessed... ;)

> If so, where am I wrong?  And how do these techniques make 
> for better code/coding?

Metaclasses are mind-demolition tools:

http://www.python.org/2.2/descrintro.html#metaclasses

As a CS student, you're probably familiar with the concept of a
'factory': a block of code which forms objects of a given class. In
Python, classes are also objects. A metaclass can be thought of as a
'class factory': a factory which produces classes (as opposed to
instances of classes). To say it another way, a class can loosely be
considered a template for objects; a metaclass is a template for
classes.

Metaclasses allow you to customize the creation of classes. A common use
is when you have a base class, let'a call it A, and you want to provide
a behavior for each subclass "class B(A)", C, D... For example, you
might want to 'register' each subclass in a module-level list. You can
do this without metaclasses:

class A(object):
    pass

class B(A):
    custom_data = {}

class C(A):
    funky_stuff = []

subclasses_of_A = [B, C]

...but that can get tedious, especially if you aren't writing the
subclasses (perhaps you're writing a framework). Whenever anyone writes
a subclass (perhaps in a different module), they have to remember to
append their new subclass to subclasses_of_A.

Metaclasses solve this by customizing class-creation code. In our
registration example, we might write:

subclasses_of_A = []

class RegisterA(type):
    def __init__(cls, name, bases, dct):
        subclasses_of_A.append(cls)

class A(object):
    __metaclass__ = RegisterA

class B(A):
    custom_data = {}

class C(A):
    funky_stuff = []

Testing this (we'll save our module as 'metatest.py'), we obtain
(PythonWin 2.3.2):

>>> import metatest
>>> metatest.subclasses_of_A
[<class 'metatest.A'>, <class 'metatest.B'>, <class 'metatest.C'>]

When we declare class A, we are creating a new object 'A' which happens
to be a class. By giving it a __metaclass__ attribute, we can customize
the creation of class A. RegisterA doesn't override type.__new__, so
type.__new__ forms the new class as usual. RegisterA does override
__init__, so RegisterA.__init__ gets called with our new (empty) class A
as the first argument, at which point we append metatest.A to our list.
I'll leave you to figure out how to exclude A but keep all the
subclasses ;) Exercise 2 would be to make this mechanism generic (that
is, keep subclasses_of_A separate from subclasses_of_Thing).

================================

Decorators are a very recent discussion; you might want to browse recent
threads on python-dev for a lot more information. You should certainly
read PEP 318 (http://www.python.org/peps/pep-0318.html). Basically, a
decorator is a way to transform a function. Again, this can usually be
done without metaclasses. The most-frequently-discussed examples are
staticmethods and classmethods:

class Thing(object):
    def newThing(cls, attr):
        t = cls()
        t.attr = attr
        return t
    newThing = classmethod(newThing)

Decorators will hopefully provide a clearer syntax for saying the same
thing (although the exact syntax is still up for debate):

class Thing(object):
    def newThing(cls, attr) [classmethod]:
        t = cls()
        t.attr = attr
        return t

This moves the transformation ('classmethod') closer to the function
definition, rather than placing it at the end of the function. In
addition, you don't have to write the name 'newThing' three times. Also,
multiple transforms could be defined at once.

Does that help?


Waiting expectantly for a heapin' helpin' of picked nits,

Robert Brewer
MIS
Amor Ministries
fumanchu at amor.org




More information about the Python-list mailing list