2010-06-09 22 views
56

Pourquoi est-ce:Métaclasse incohérence héritage multiple

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

D'accord, et fonctionne comme prévu:

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

Mais ceci:

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

est pas bien, et explose 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 

Répondre

81

Ce n'est pas un problème métaclasse personnalisé (bien qu'il soit diagnostiqué au stade de métaclasse):

>>> 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 

et le problème est la même chose que celui-ci:

>>> 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 

-à-dire, ne peut pas multipliez l'héritage d'une classe de base suivie d'une classe dérivée - il est impossible de définir un MRO cohérent qui satisfasse les contraintes/garanties MRO habituelles.

Heureusement, vous ne voulez de le faire - la sous-classe l'emporte sans doute une méthode de la classe de base (qui est ce que les sous-classes normales font ;-), et ayant la classe de base « devant » serait signifie "occulter le remplacement".

En mettant la classe de base après la dérivée est assez inutile, mais au moins elle est inoffensive (et conforme aux garanties MRO normales).

Votre premier exemple bien sûr fonctionne parce que MyMixin est pas dérivé de list:

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

... mais il est dérivé de object (comme toutes les classes Python style moderne), de sorte que le deuxième exemple ne peut pas fonctionner (tout à fait indépendamment de MyMixin ayant une métaclasse personnalisée).

-1

Ici, vous héritez de la classe parente, et la classe parente hérite déjà d'une autre classe, il n'est donc pas nécessaire d'hériter de la classe dont la classe parente a déjà hérité.

Par exemple:

class A(object): 
. 
. 
class B(object, A): 
. 
. 

Il lancera une erreur parce que A est héritant de la classe Object et B hérite de A, de façon indirecte B hérite d'objet, donc il n'y a pas besoin d'hériter objet. . . .

La solution consiste à supprimer simplement la classe d'objets de la liste d'arguments de classe B ....