Python 元类行为(不调用 __new__),有解释吗?
Posted
技术标签:
【中文标题】Python 元类行为(不调用 __new__),有解释吗?【英文标题】:Python metaclass behavior (not calling __new__), is there an explanation? 【发布时间】:2015-04-12 12:12:58 【问题描述】:在一个名为 exp.py(如下)的文件中,我试图理解 Python 中的元类。似乎当元类的__new__
方法使用“类型”构造函数来构造一个类时,它的__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>
>>>
如您所见,元类 D 的 __new__
方法在加载类 F 的定义时不会被调用。 (如果它被调用了,那么关于 F 类的信息也会被打印出来。)
有人可以帮我解释一下吗?
相关帖子:我在看What is a metaclass in Python?,好像在这句话中遇到了类似的东西,“注意这里__metaclass__
属性不会被继承,父类(Bar.__class__
)的元类会是. 如果 Bar 使用 __metaclass__
属性创建 Bar 与 type()
(而不是 type.__new__()
),子类将不会继承该行为。但我并没有完全理解这一点。
【问题讨论】:
【参考方案1】:在第二种情况下,实际上不是返回元类的实例,而是返回type
的实例,这反过来又将新创建的类E
的__class__
属性设置为<type 'type'>
,而不是D
。因此as per the rule 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 旧式类没有此属性,甚至type()
的输出也不同。对于新式类,默认类型是type
,对于旧式类,默认类型是classobj
。 New-style and classic classes以上是关于Python 元类行为(不调用 __new__),有解释吗?的主要内容,如果未能解决你的问题,请参考以下文章