Metaclass mystery
LittleGrasshopper
seattlehanks at yahoo.com
Sat May 30 21:37:13 EDT 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