【问题标题】:Base metaclass overriding __new__ generates classes with a wrong __module__覆盖 __new__ 的基元类生成具有错误 __module__ 的类
【发布时间】:2012-06-26 14:23:47
【问题描述】:

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

给定一个模块 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 类模块错误!

这是预期的行为吗?

有没有办法覆盖不会引发问题的new

谢谢,

克里斯托夫

【问题讨论】:

    标签: python metaclass


    【解决方案1】:

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

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

    也就是说,我相信您可以通过这样做来获得您想要的行为,例如,

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

    【讨论】:

    • 问题是,如果我不覆盖__new__,则调用type.__new__,在这种情况下自动__module__ 是正确的。这很奇怪。
    • 至于我为什么要这样使用元类,是因为它是唯一兼容python 2和3的方式。
    • @ChristophedeVienne:你所说的type.__new__ 是正确的,可能是因为type.__new__ 是内置的,所以通常的“它在哪个模块中”的概念不适用。不幸的是,在 Python 中,您不能总是依赖基本的内置行为与明显无操作覆盖该行为所获得的行为相同。
    • @ChristophedeVienne:我不知道你对 Python 2 和 3 的兼容性是什么意思。你想做什么在不同版本中不起作用?你最好使用 2to3 转换器而不是自己动手。
    • 我在一个可以在 python 2 和 3 上工作的库中使用元类,而不使用 py2to3。我在 python3porting.com 中找到了我使用元类的方式(我不是在做那种事情,因为我希望我的代码是可维护的),但从那时起它已经改为建议使用 six 来做到这一点。我认为我会走这条路(尽管需要先尝试),尽管您提出的解决方法有效(我已经测试过)。
    猜你喜欢
    • 1970-01-01
    • 2016-04-19
    • 2015-12-11
    • 1970-01-01
    • 2019-12-06
    • 2017-07-05
    • 1970-01-01
    • 2011-04-21
    • 2018-07-31
    相关资源
    最近更新 更多