使用元类和多重继承的TypeErrors

时间:2010-02-04 23:13:45

标签: python multiple-inheritance python-2.x metaclass

我有两个问题,即转换元类和多重继承。第一个是:为什么我为类Derived而不是Derived2获取TypeError?

class Metaclass(type): pass

class Klass(object):
    __metaclass__  = Metaclass

#class Derived(object, Klass): pass # if I uncomment this, I get a TypeError

class OtherClass(object): pass

class Derived2(OtherClass, Klass): pass # I do not get a TypeError for this

确切的错误消息是:

TypeError: Error when calling the metaclass bases Cannot create a consistent method resolution order (MRO) for bases object, Klass

第二个问题是:为什么super在这种情况下不起作用(如果我使用__init__代替__new__super再次有效):

class Metaclass(type):
    def __new__(self, name, bases, dict_):
        return super(Metaclass, self).__new__(name, bases, dict_)

class Klass(object):
    __metaclass__  = Metaclass

我得到了:

TypeError: Error when calling the metaclass bases type.__new__(X): X is not a type object (str)

我正在使用Python 2.6。

4 个答案:

答案 0 :(得分:7)

第二个问题已经两次得到很好的回答,但__new__实际上是一种静态方法,而不是评论中错误声明的类方法......:

>>> class sic(object):
...   def __new__(cls, *x): return object.__new__(cls, *x)
... 
>>> type(sic.__dict__['__new__'])
<type 'staticmethod'>

第一个问题(正如有人指出的那样)与元类无关:你根本无法在这个顺序中从任何两个类A和B中继承,其中B是A的子类。例如:

>>> class cis(sic): pass
... 
>>> class oops(sic, cis): 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 sic, cis

MRO保证在最右边的基地之前访问最左边的基地 - 但是它也保证了在祖先中如果x是y的子类,则在y之前访问x。在这种情况下,不可能满足这两种保证。这些保证当然有充分的理由:没有它们(例如在旧式类中,只保证方法解析中的左右顺序,子类约束)x中的所有覆盖都将被忽略支持y中的定义,这没有多大意义。想想看:做什么首先从object继承,从其他第二类继承?它的几个特殊方法的object(基本上不存在;-)定义必须优先于其他类,导致其他类的覆盖被忽略?

答案 1 :(得分:3)

对于第一个问题,请查看the description of MRO in python - 特别是“错误的方法解析顺序”部分。从本质上讲,这与python不知道是否使用object或Klass方法的事实有关。 (这与元类的使用无关。)

对于第二个问题,您似乎误解了__new__函数的工作原理。它不会将自身引用作为第一个参数 - 它引用要实例化的类的类型。所以你的代码应该是这样的:

class Metaclass(type):
    def __new__(cls, name, bases, dictn):
        return type.__new__(cls, name, bases, dictn)

答案 2 :(得分:0)

对于第二个问题,您需要将自己传递给__new__,如下所示:

class Metaclass(type):
    def __new__(self, name, bases, dict_):
        return super(Metaclass, self).__new__(self, name, bases, dict_)

class Klass(object):
    __metaclass__  = Metaclass

我无法回想起为什么会这样,但我认为这是因为type.__new__不是一个绑定方法,因此不会神奇地得到自我论证。

答案 3 :(得分:0)

你为什么这样做?

class Derived(object, Klass):

Klass已经从对象派生出来。

class Derived(Klass):

这里是否合理。