使用元类和多重继承时的类型错误

4 投票
4 回答
7535 浏览
提问于 2025-04-15 18:54

我有两个关于 metaclasses(元类)和多重继承的问题。第一个问题是:为什么我在使用 Derived 类时会出现 TypeError 错误,而在使用 Derived2 时却没有?

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: 调用元类基础时出错 无法为基础对象、Klass 创建一致的方法解析顺序(MRO)

第二个问题是:为什么在这种情况下 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: 调用元类基础时出错 type.__new__(X): X 不是一个类型对象(str)

我使用的是 Python 2.6。

4 个回答

0

你为什么要这么做呢?

class Derived(object, Klass):

这个类已经是从对象(object)派生出来的了。

class Derived(Klass):

这样做是合理的。

4

对于第一个问题,可以看看Python中MRO的描述,特别是“坏的方法解析顺序”这一部分。简单来说,这个问题是因为Python不确定是使用对象的方法还是Klass的方法。(这和元类的使用没有关系。)

对于第二个问题,似乎你对__new__函数的工作原理有些误解。它的第一个参数并不是指向它自己的引用,而是指向正在实例化的类的类型。所以你的代码应该像这样:

class Metaclass(type):
    def __new__(cls, name, bases, dictn):
        return type.__new__(cls, name, bases, dictn)
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 的子类,那么 x 会在 y 之前被访问。在这种情况下,无法同时满足这两个保证。当然,这些保证是有原因的:如果没有它们(比如在旧式类中,只保证了方法解析的左右顺序,而是子类约束),那么 x 中的所有重写都会被 y 中的定义所忽略,这样就没有什么意义了。想想看:首先从 object 继承,然后再从其他类继承,这到底意味着什么呢?这是不是意味着 object 的(几乎不存在的;-) 特殊方法的定义必须优先于其他类的定义,导致其他类的重写被忽略?

撰写回答