About Michele Simionato's "classinitializer" decorator

imho certo at comeno.it
Mon May 26 13:10:46 EDT 2008


Hi all.
I just send this post as a comment to the third part of Michele 
Simionato's article about metaprogramming techniques at:

http://www.ibm.com/developerworks/linux/library/l-pymeta3.html?S_TACT=105AGX03&S_CMP=ART


I found extremely useful the classinitializer "trick". I was thinking 
over the caveat not to call a class initializer after the _metaclass_ 
hook (an exception being raised in that case).
Am I wrong if I state there's a plain solution to this ? I modified the 
lines involving "type", replacing it with a custom meta type returned by 
a call to frame.f_locals.get("__metaclass__", type), in such a way the 
creation of the class involves the right metaclas, even if it was 
defined  just before the initializer function:

def classinitializer(proc):
     # basic idea stolen from zope.interface.advice, P.J. Eby
     def newproc(*args, **kw):
        frame = sys._getframe(1)
        if '__module__' in frame.f_locals and not \
            '__module__' in frame.f_code.co_varnames: # we are in a class
            thetype = frame.f_locals.get("__metaclass__", type)
            def makecls(name, bases, dic):
                try:
                    cls = thetype(name, bases, dic)
                except TypeError, e:
                    if "can't have only classic bases" in str(e):
                        cls = thetype(name, bases + (object,), dic)
                    else:  # other strange errs, e.g. __slots__ conflicts
                        raise
                proc(cls, *args, **kw)
                return cls
            frame.f_locals["__metaclass__"] = makecls
        else:
            proc(*args, **kw)
     newproc.__name__ = proc.__name__
     newproc.__module__ = proc.__module__
     newproc.__doc__ = proc.__doc__
     newproc.__dict__ = proc.__dict__
     return newproc

This makes the trick work even if a metaclass or another 
classinitializer was defined first:

@classinitializer
def enhance(cls, **kw):
     for k, v in kw.iteritems():
         setattr(cls, k, v)

class M(type):
     pass

class A:
     __metaclass__ = M
     enhance(x=100)
     enhance(y=200)


 >>> A.x
100
 >>> A.y
200
 >>> type(A)
<class '__main__.M'>

What do you think about this solution ?

Diego Novella.



More information about the Python-list mailing list