Python 元类行为(不调用 __new__),有解释吗?

Python metaclass behavior (not calling __new__), is there an explanation?

在名为 exp.py 的文件(下)中,我试图理解 Python 中的元 classes。似乎当 metaclass 的 __new__ 方法使用 'type' 构造函数构造 class 时,它的 __new__ 方法不会被子调用class 的 class 使用它作为 metaclass:

class A(type):
    def __new__(cls, name, bases, dct):
        print "cls is:   ", cls
        print "name is:  ", name
        print "bases is: ", bases
        print "dct is:   ", dct
        print
        return super(A, cls).__new__(cls, name, bases, dct)

class B(object):
    __metaclass__ = A

class C(B): pass

class D(type):
    def __new__(cls, name, bases, dct):
        print "cls is:   ", cls
        print "name is:  ", name
        print "bases is: ", bases
        print "dct is:   ", dct
        return type(name, bases, dct)

class E(object):
    __metaclass__ = D

class F(E): pass

在终端中:

>>> from exp import *
cls is:    <class 'exp.A'>
name is:   B
bases is:  (<type 'object'>,)
dct is:    {'count': 0, '__module__': 'exp', '__metaclass__': <class 'exp.A'>, '__init__': <function __init__ at 0x107eb9578>}

cls is:    <class 'exp.A'>
name is:   C
bases is:  (<class 'exp.B'>,)
dct is:    {'__module__': 'exp'}

cls is:    <class 'exp.D'>
name is:   E
bases is:  (<type 'object'>,)
dct is:    {'count': 0, '__module__': 'exp', '__metaclass__': <class 'exp.D'>, '__init__': <function __init__ at 0x107ebdb18>}
>>> 

如您所见,加载 class F 的定义时,元 class D 的 __new__ 方法不会被调用。 (如果它被调用,那么关于 class F 的信息也会被打印出来。)

谁能帮我解释一下?


相关post:我在看What is a metaclass in Python?,好像在句子中遇到了类似的东西,"Be careful here that the __metaclass__ attribute will not be inherited, the metaclass of the parent (Bar.__class__) will be. If Bar used a __metaclass__ attribute that created Bar with type() (and not type.__new__()), the subclasses will not inherit that behavior."但是我没有完全理解这个。

在第二种情况下,您实际上返回的不是元 class 的实例,而是 type 的实例,这反过来又设置了新的 __class__ 属性将 class E 创建为 <type 'type'> 而不是 D。因此 as per the rule 2 Python 检查基础 class 的 __class__ 属性(或 type(E)E.__class__)并决定使用 type作为 F 的元 class,因此在这种情况下永远不会调用 D__new__

  • 如果 dict['__metaclass__'] 存在,则使用它。
  • 否则,如果至少有一个基class,则使用其元class(这会查找__class__属性首先,如果未找到,则使用其类型)。

所以,正确的方法是在metaclass的__new__方法中调用type的__new__方法:

class D(type):
    def __new__(cls, name, bases, dct):
        print "cls is:   ", cls
        print "name is:  ", name
        print "bases is: ", bases
        print "dct is:   ", dct
        return type.__new__(cls, name, bases, dct)

class E(object):
    __metaclass__ = D

class F(E): pass

class A(object):
    pass

输出:

cls is:    <class '__main__.D'>
name is:   E
bases is:  (<type 'object'>,)
dct is:    {'__module__': '__main__', '__metaclass__': <class '__main__.D'>}
cls is:    <class '__main__.D'>
name is:   F
bases is:  (<class '__main__.E'>,)
dct is:    {'__module__': '__main__'}