problem in using metaclasses to inspect Python code

Greg Chapman glc at well.com
Fri Dec 13 10:40:34 EST 2002


On 12 Dec 2002 11:36:37 -0800, mis6 at pitt.edu (Michele Simionato) wrote:

>If I write
>
>class Meta(type):
>    def __init__(cls,name,bases,dict):
>        print "%s called me!" % name
>
>class C(object):
>    __metaclass__=Meta
>
>then the output of this program is what you expect:
>
>C called me!
>
>However, if I write
>
>class D(object):
>    pass
>
>D.__metaclass__=Meta
>
>I DON'T obtain what I expect, i.e. "D called me!": the metaclass
>Meta is not automatically called. How do I invoke Meta explicitly ?

Ignoring metaclasses for the moment, suppose we had:

class C(object):
    pass

class D(C):
    pass

c = C()

Now suppose you want to change the __class__ of c from C to D.  You can do this
in Python:

c.__class__ = D

but you certainly wouldn't expect c's __init__ method to be called again (at
least I hope you ouldn't).

The case is just the same for metaclasses:

class D(object):
    pass

The above class statement creates D as an instance of the metaclass 'type.'  The
metaclass is determined by looking at the 'object' class and discovering that
its __class__ is 'type'.  When you include a __metaclass__ attr in a class
declaration, you override this to say you want to use the given metaclass as the
__class__ of the class which is being created (the __metaclass__ attr has no
effect once the new class is created).  Just as when you create an instance of
an object you call the object's class (i.e., c = C()), to create an instance of
a class, Python calls the class's class (the metaclass).  And just as when you
call a class to create an instance, the call goes through the class's __new__
and __init__ methods, when you call a metaclass to create a class, the call goes
through the metaclass's __new__ and __init__ methods.

class Meta(type):
    def sayHi(cls):
        print 'hi from Meta'
        print 'class instance is', cls.__name__

The above class statement creates a subtype of the 'type' metaclass.

D.__class__ = Meta
D.sayHi()          #prints 'hi from Meta\nclass instance is D'

The above changes the metaclass of the type instance D from type to Meta

Anyway, just as for instances, you would not expect D's __init__ (as defined in
its metaclass) to be called again when you change its __class__.

I hope I'm not belaboring this too much, but you could do away with the class
statement entirely if you wanted.  E.g.:

>>> D = type('D', (object,), {})
>>> def sayHi(cls):
...     print 'hi from Meta'
...     print 'class instance is', cls.__name__
...
>>> Meta = type('Meta', (type,), {'sayHi': sayHi})
>>> D.__class__ = Meta
>>> D.sayHi()
hi from Meta
class instance is D
>>> E = Meta('E', (), {})
>>> E.sayHi()
hi from Meta
class instance is E
>>> e = E()
>>> e.__class__
<class '__main__.E'>
>>> e.__class__.__class__
<class '__main__.Meta'>

---
Greg Chapman




More information about the Python-list mailing list