你猜测很多,而Python的极简主义和“特殊情况不足以破坏规则”.指令,让它更容易理解.
在Python2中,类主体中的__metaclass__属性在类创建时用于调用类所属的“类”.通常它是名为type的类.为了澄清,那一刻是在解析器解析了类体之后,在编译器将其编译为代码对象之后,以及在程序运行时实际运行之后,并且只有在该类体中明确提供了__metaclass__之后.
所以让我们检查方式如下:
class A(object):
__metaclass__ = MetaA
class B(A):
pass
A在其正文中有__metaclass__ – 调用MetaA而不是类型,使其成为“类对象”.
B的体内没有__metaclass__.创建它之后,如果你只是尝试访问__metaclass__属性,那么它就是一个属性,它将是可见的,因为Python将从超类A中获取它.如果你检查A .__ dict__,你会看到__metaclass__,如果你检查B .__ dict__没有.
创建B时,根本不使用此A .__ metaclass__属性.如果在声明之前在A中更改它仍然会使用与A相同的元类 – 因为Python确实使用父类的类型作为元类,而忽略了显式__metaclass__的声明.
为了显示:
In [1]: class M(type): pass
In [2]: class A(object): __metaclass__ = M
In [3]: print "class: {}, metaclass_attr: {}, metaclass_in_dict: {}, type: {}".format(A.__class__, A.__metaclass__, A.__dict__.get("__metaclass__"), type(A))
class: , metaclass_attr: , metaclass_in_dict: , type:
In [4]: class B(A): pass
In [5]: print "class: {}, metaclass_attr: {}, metaclass_in_dict: {}, type: {}".format(B.__class__, B.__metaclass__, B.__dict__.get("__metaclass__"), type(B))
class: , metaclass_attr: , metaclass_in_dict: None, type:
In [6]: A.__metaclass__ = type
In [8]: class C(A): pass
In [9]: print "class: {}, metaclass_attr: {}, metaclass_in_dict: {}, type: {}".format(C.__class__, C.__metaclass__, C.__dict__.get("__metaclass__"), type(C))
class: , metaclass_attr: , metaclass_in_dict: None, type:
此外,如果您尝试通过调用类型而不是使用带有类语句的主体来创建类,则__metaclass__也只是一个普通属性:
In [11]: D = type("D", (object,), {"__metaclass__": M})
In [12]: type(D)
type
总结到目前为止:Python 2中的__metaclass__属性只有在显式放置在类主体声明中时才是特殊的,作为执行类块语句的一部分.它是一个普通的属性,之后没有特殊的属性.
Python3都摆脱了这个奇怪的“__metaclass__属性现在不好”,并允许通过更改语法来指定元类来进一步自定义类体. (就像在类声明本身上声明它是一个“元类命名参数”)
现在,对于提出疑问的第二部分:如果在元类的__new__方法中调用类型而不是类型.__ new__,则Python无法“知道”类型是从派生元类调用的.当你调用type .__ new__时,你传递你的元类的__new__本身被运行时传递的cls属性作为它的第一个参数:这就是将结果类标记为类的子类的实例.这就像继承适用于Python中的任何其他类一样 – 所以“没有特殊行为”:
所以,发现差异:
class M1(type):
def __new__(metacls, name, bases, attrs):
cls = type.__new__(metacls, name, bases, attrs)
# cls now is an instance of "M1"
...
return cls
class M2(type):
def __new__(metacls, name, bases, attrs):
cls = type(cls, name, bases, attrs)
# Type does not "know" it was called from within "M2"
# cls is an ordinary instance of "type"
...
return cls
可以在交互式提示中看到:
In [13]: class M2(type):
....: def __new__(metacls, name, bases, attrs):
....: return type(name, bases, attrs)
....:
In [14]: class A(M2): pass
In [15]: type(A)
Out[15]: type
In [16]: class A(M2): __metaclass__ = M2
In [17]: A.__class__, A.__metaclass__
Out[17]: (type, __main__.M2)
(请注意,元类__new__方法的第一个参数是元类本身,因此比代码中的cls更恰当地命名为metacls,并且在“野外”中有很多代码)