Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Metaclass multiple inheritance inconsistency

Why is this:

class MyType(type):     def __init__(cls, name, bases, attrs):         print 'created', cls class MyMixin:     __metaclass__ = MyType class MyList(list, MyMixin): pass 

okay, and works as expected:

created <class '__main__.MyMixin'> created <class '__main__.MyList'> 

But this:

class MyType(type):     def __init__(cls, name, bases, attrs):         print 'created', cls class MyMixin:     __metaclass__ = MyType class MyObject(object, MyMixin): pass 

Is not okay, and blows up thusly?:

created <class '__main__.MyMixin'> Traceback (most recent call last):   File "/tmp/junk.py", line 11, in <module>     class MyObject(object, MyMixin): pass TypeError: Error when calling the metaclass bases     Cannot create a consistent method resolution order (MRO) for bases object, MyMixin 
like image 310
Matt Anderson Avatar asked Jun 09 '10 04:06

Matt Anderson


1 Answers

It's not a custom-metaclass problem (though it's diagnosed at metaclass stage):

>>> class Normal(object): pass ...  >>> class MyObject(object, Normal): pass ...  Traceback (most recent call last):   File "<stdin>", line 1, in <module> TypeError: Error when calling the metaclass bases     Cannot create a consistent method resolution order (MRO) for bases object, Normal 

and the problem's just the same as this one:

>>> class Derived(Normal): pass ...  >>> class Ok(Derived, Normal): pass ...  >>> class Nope(Normal, Derived): pass ...  Traceback (most recent call last):   File "<stdin>", line 1, in <module> TypeError: Error when calling the metaclass bases     Cannot create a consistent method resolution order (MRO) for bases Normal, Derived 

i.e., can't multiply inherit from a base class followed by a derived class -- it's impossible to define a consistent MRO that satisfies the usual MRO constraints/guarantees.

Fortunately, you don't want to do that -- the subclass presumably overrides some method of the base class (that's what normal subclasses do;-), and having the base class "in front" would mean "shadowing the override away".

Putting the base class after the derived one is pretty useless, but at least it's innocuous (and consistent with normal MRO guarantees).

Your first example of course works because MyMixin is not derived from list:

>>> MyMixin.__mro__ (<class '__main__.MyMixin'>, <type 'object'>) 

...but it is derived from object (like every modern-style Python class), so the second example cannot work (quite independently from MyMixin having a custom metaclass).

like image 82
Alex Martelli Avatar answered Oct 30 '22 11:10

Alex Martelli