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

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

在一个名为exp.py的文件中(下面),我试图理解Python中的元类。似乎当元类的__new__方法使用"type"构造函数构造类时,其__new__方法不会被用作元类的类的子类调用:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
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

在终端中:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
>>> 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>}
>>>

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

有人能帮我解释一下吗?

相关文章:我在读什么是Python中的元类?并且在句子中似乎遇到了类似的情况,"注意这里的__metaclass__属性不会被继承,父类(Bar.__class__的元类将被继承。"如果bar使用了type()而不是type.__new__()创建bar的__metaclass__属性,那么子类将不会继承该行为。"但我并不完全理解这一点。


在第二种情况下,不是返回元类的实例,而是实际返回type的实例,而是将新创建的类E__class__属性设置为,而不是D。因此,根据规则2,python检查基类的__class__属性(或type(E)E.__class__),并决定使用type作为F的元类,因此在这种情况下,不会调用D__new__

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

因此,正确的方法是在元类的__new__方法中调用类型的__new__方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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

输出:

1
2
3
4
5
6
7
8
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__'}