Metaclass mystery

LittleGrasshopper seattlehanks at yahoo.com
Sun May 31 03:37:13 CEST 2009


On May 30, 6:15 pm, Carl Banks <pavlovevide... at gmail.com> wrote:
> On May 30, 5:32 pm, LittleGrasshopper <seattleha... at yahoo.com> wrote:
>
>
>
> > On May 30, 4:01 pm, LittleGrasshopper <seattleha... at yahoo.com> wrote:
>
> > > I am experimenting with metaclasses, trying to figure out how things
> > > are put together. At the moment I am baffled by the output of the
> > > following code:
>
> > > ************************************
> > > """
> > > Output is:
>
> > > instance of metaclass MyMeta being created
> > > (<class '__main__.MyMeta'>, <class '__main__.MyMeta'>)
> > > instance of metaclass MyNewMeta being created
> > > instance of metaclass MyMeta being created   <<<< Why this?
> > > (<class '__main__.MyMeta'>, <class '__main__.MyNewMeta'>)
>
> > > """
>
> > > class MyMeta(type):
> > >     def __new__(meta, classname, bases, classDict):
> > >         print 'instance of metaclass MyMeta being created'
> > >         return type.__new__(meta, classname, bases, classDict)
>
> > > class MyNewMeta(type):
> > >     def __new__(meta, classname, bases, classDict):
> > >         print 'instance of metaclass MyNewMeta being created'
> > >         return type(classname, bases, classDict)
>
> > > """
> > > Notice that a metaclass can be a factory function:
> > > def f(classname, bases, classDict):
> > >     return type(classname, bases, classDict)
>
> > > class MyClass(object):
> > >     __metaclass__ = f
> > > """
>
> > > class MyClass(object):
> > >     __metaclass__ = MyMeta
>
> > > print (MyClass.__class__, MyClass.__metaclass__)
>
> > > class MySubClass(MyClass):
> > >     __metaclass__ = MyNewMeta
>
> > > print (MySubClass.__class__, MySubClass.__metaclass__)
> > > ************************************
> > > Honestly, I don't know why this line:
> > > instance of metaclass MyMeta being created   <<<< Why this?
> > > is being output when the MySubClass class object is instantiated.
> > > MyNewMeta's __new__ method simply instantiates type directly (which I
> > > know shouldn't be done, but I'm just experimenting and trying to
> > > understand the code's output.)
>
> > > I would really appreciate some ideas.
>
> > This is my working theory:
>
> > return type(classname, bases, classDict), in MyNewMeta.__new__(),
> > actually calls type.__new__(type, classname, bases, classDict). I
> > think the magic happens in this method call. This method must look at
> > bases and notice that MySubClass extends MyClass, and that MyClass'
> > type is MyMeta, so instead of instantiating a 'type' object it decides
> > to instantiate a 'MyMeta' object, which accounts for the output.
>
> That's correct.  A type's metaclass has to be a not-necessarily proper
> superclass of the all the bases' metaclasses.
>
> Whenever possible type() will figure the most derived metaclass of all
> the bases and use that as the metaclass, but sometimes it can't be
> done.  Consider the following:
>
> class AMeta(type):
>     pass
>
> class A(object):
>     __metaclass__ = AMeta
>
> class BMeta(type):
>     pass
>
> class B(object):
>     __metaclass__ = BMeta
>
> class C(A,B):
>     pass  # this will raise exception
>
> class CMeta(AMeta,BMeta):
>     pass
>
> class C(A,B):
>     __metaclass__ = CMeta # this will work ok
>
> > Seriously, metaclasses are making my brain hurt. How do people like
> > Michele Simionato and David Mertz figure these things out? Does it all
> > come to looking at the C source code for the CPython interpreter?
>
> > Brain hurts, seriously.
>
> I actually did rely on looking at the C source.  There's a surprising
> amount of checking involving metaclass, layout, special methods, and
> so on that is involved when creating a new type.
>
> Carl Banks

Thanks a lot, Carl. It's funny that you would post that example, as
just 15 minutes ago I wrote almost the exact same code in my testing:

class XMeta(type):
    pass

class YMeta(type):
    pass

class X(object):
    __metaclass__ = XMeta

class Y(object):
    __metaclass__ = YMeta

"""
This causes an error, since there is no leafmost metaclass in the set
{XMeta, YMeta}:

class Z(X, Y):
    pass
"""

"""
The way to solve this is to create a common derived metaclass and use
that
one for class z:
"""

class ZMeta(XMeta, YMeta):
    pass

class Z(X, Y):
    __metaclass__ = ZMeta

Looking at the C code sounds like a daunting task, but I might take a
plunge and give it a try, since it seems that guessing via
experimental results is not the most optimal way to go about it.

Thank you again.



More information about the Python-list mailing list