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

3
在名为exp.py的文件中(如下所示),我正在尝试理解Python中的元类。当元类的__new__方法使用'type'构造函数来构造一个类时,似乎它的__new__方法不会被使用它作为元类的类的子类调用:
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>}
>>> 

正如您所看到的,当类F被加载时,元类D的__new__方法并未被调用。(如果已经调用,则会打印有关类F的信息。)请问是否有人可以帮我解释一下这个问题?

相关文章:我正在阅读Python中的元类是什么?,在这句话中似乎遇到了类似的东西,“请注意,在这里__metaclass__属性不会被继承,父类(Bar.__class__)的元类会被继承。如果Bar使用一个创建Bar的type()(而不是type.__new__())的__metaclass__属性,那么子类将不会继承该行为。”但是我没有完全理解。

1个回答

4
在第二种情况下,你实际上返回的是type的一个实例,而不是元类的实例。这反过来将新创建的类E__class__属性设置为<type 'type'>,而不是D。因此,根据规则2 Python会检查基类的__class__属性(或type(E)E.__class__),并决定使用type作为F的元类,所以在这种情况下不会调用D__new__
  • 如果dict['__metaclass__']存在,则使用它。
  • 否则,如果至少有一个基类,则使用其元类(首先查找__class__属性,如果未找到,则使用其类型)。
因此,正确的方法是在元类的__new__方法中调用类型的__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__'}

非常好的答案,谢谢!顺便问一下,我对这个短语有些问题:“(首先查找__class__属性,如果没有找到,则使用其类型)”。1.在什么情况下会找不到__class__属性?2.在什么情况下类的__class__属性会与其类型(使用type()确定)不同?谢谢。 - platypus
@platypus 旧式类别没有这个属性,即使它们的 type() 输出也不同。对于新式类别,默认类型是 type,而对于旧式类别,则为 classobj新式和经典类别 - Ashwini Chaudhary

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接