1

我发现 cpython 2.5、2.7、3.2 和带有 metaclass 的 pypy 的奇怪行为__new__在使用 python 2 / python 3 兼容的方式使用 metaclass 时会被覆盖:

给定一个模块 m1:

class C1Meta(type):
    def __new__(cls, name, bases, dct):
        return type.__new__(cls, name, bases, dct)

C1 = C1Meta('C1', (object,), {})


class C2Meta(type):
    pass

C2 = C2Meta('C2', (object,), {})

以及以下主程序:

import m1

C10 = m1.C1Meta('C10', (m1.C1,), {})


class C11Meta(m1.C1Meta):
    pass

C11 = C11Meta('C11', (m1.C1,), {})


class C12Meta(m1.C1Meta):
    def __new__(cls, name, bases, dct):
        return m1.C1Meta.__new__(cls, name, bases, dct)

C12 = C12Meta('C12', (m1.C1,), {})


class C13Meta(m1.C1Meta):
    def __new__(cls, name, bases, dct):
        return type.__new__(cls, name, bases, dct)

C13 = C13Meta('C13', (m1.C1,), {})


C20 = m1.C2Meta('C20', (m1.C2,), {})


class C21Meta(m1.C2Meta):
    pass

C21 = C21Meta('C21', (m1.C2,), {})


class C22Meta(m1.C2Meta):
    def __new__(cls, name, bases, dct):
        return m1.C2Meta.__new__(cls, name, bases, dct)

C22 = C22Meta('C22', (m1.C2,), {})


class C23Meta(m1.C2Meta):
    def __new__(cls, name, bases, dct):
        return type.__new__(cls, name, bases, dct)

C23 = C23Meta('C23', (m1.C2,), {})


print(C10)
print(C11)
print(C12)
print(C13)

print(C20)
print(C21)
print(C22)
print(C23)

运行脚本将产生以下输出(包含所有提到的 python 版本):

<class 'm1.C10'>
<class 'm1.C11'>
<class 'm1.C12'>
<class '__main__.C13'>
<class '__main__.C20'>
<class '__main__.C21'>
<class '__main__.C22'>
<class '__main__.C23'>

-> C10、C11 和 C12 类模块是错误的!

这是预期的行为吗?

有没有办法覆盖不会引发问题的新方法?

谢谢,

克里斯托夫

4

1 回答 1

1

显然,该__module__属性是在元类执行时设置的。在您的情况下,元类在内部执行m1。对于正常定义的类,__module__属性会自动生成并传递给元类。但是,您正在通过手动调用元类来创建类,并传入一个的属性字典。因此,您的类不提供__module__属性。该__module__属性是自动创建的,但直到type.__new__被调用。由于这发生在模块内部m1,因此当时的__module__created 指的是m1.

我不确定您为什么要明确调用元类。元类的全部意义在于允许您自定义使用class语句定义类时发生的情况。如果您不想使用class语句,您可以只使用常规函数来创建您的类,而根本不需要使用元类。

也就是说,我相信您可以通过以下方式获得所需的行为,例如,

C11 = C11Meta('C11', (m1.C1,), {'__module__': __name__})
于 2012-08-25T21:16:43.517 回答