元类多重继承不一致性

59 投票
2 回答
18406 浏览
提问于 2025-04-15 23:43

为什么这个:

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

2 个回答

-1

在这里,你是从父类继承的,而这个父类已经继承了另一个类,所以你不需要再去继承父类已经继承的那个类。

举个例子:

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

这样会报错,因为A类继承了Object类,而B类又继承了A类,所以B类间接地也继承了Object类,因此没有必要再去继承Object类。

解决办法就是直接把B类中的Object类从继承列表中去掉就可以了。

83

这不是一个自定义元类的问题(虽然在元类阶段会被“诊断”出来):

>>> 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约束和保证。

幸运的是,你其实并不想这样做——子类通常会覆盖基类的某个方法(这就是正常子类的“职责”;-),如果基类在前面,就意味着会“遮住”这个覆盖。

把基类放在派生类后面其实没什么用,但至少这样做是无害的(并且符合正常的MRO保证)。

你的第一个例子当然可以工作,因为 MyMixin 并不是从 list 继承的:

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

……但它确实是从 object 继承的(就像每个现代风格的Python类一样),所以第二个例子就无法工作了(这和 MyMixin 是否有自定义元类无关)。

撰写回答