web-dev-qa-db-ja.com

メタクラスの多重継承の不整合

どうしてこれなの:

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

さて、期待どおりに動作します:

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

しかしこれは:

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

大丈夫ではなく、こうして爆破しますか?:

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
57
Matt Anderson

これはカスタムメタクラスの問題ではありません(ただし、メタクラスの段階ではdiagnosedです)。

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

そして問題はこれと同じです:

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

つまり、基本クラスから派生クラスを続けて複数継承することはできません。通常のMRO制約/保証を満たす一貫したMROを定義することは不可能です。

幸い、あなたはwantを行わない-サブクラスはおそらく基本クラスのいくつかのメソッドをオーバーライドします(これは通常のサブクラスdo ;-)をオーバーライドし、 「前」のクラスは、「オーバーライドをシャドウイングする」ことを意味します。

基本クラスを置くafter派生したクラスはほとんど役に立ちませんが、少なくとも無害です(そして通常のMRO保証と一致しています)。

MyMixinnotlistから派生しているため、コースの最初の例は機能します。

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

...しかしisobjectから派生しているため(すべてのモダンスタイルPythonクラスと同様))、2番目の例は機能しません( MyMixinカスタムメタクラスを持つ)。

83
Alex Martelli