元类的继承
Inheritance of metaclass
在这个著名的 answer 中解释了 metaclass 在 Python 中。它提到 __metaclass__
属性不会被继承。
但事实上,我试过 Python:
class Meta1(type):
def __new__(cls, clsname, bases, dct):
print "Using Meta1"
return type.__new__(cls, clsname, bases, dct)
# "Using Meta1" printed
class Foo1:
__metaclass__ = Meta1
# "Using Meta1" printed
class Bar1(Foo1):
pass
正如预期的那样,Foo
和 Bar
都使用 Meta1
作为 metaclass 并按预期打印字符串。
但在下面的示例中,当返回 type(...)
而不是 type.__new__(...)
时,metaclass 不再被继承:
class Meta2(type):
def __new__(cls, clsname, bases, dct):
print "Using Meta2"
return type(clsname, bases, dct)
# "Using Meta2" printed
class Foo2:
__metaclass__ = Meta2
# Nothing printed
class Bar2(Foo2):
pass
检查 __metaclass__
和 __class__
属性,我可以看到:
print Foo1.__metaclass__ # <class '__main__.Meta1'>
print Bar1.__metaclass__ # <class '__main__.Meta1'>
print Foo2.__metaclass__ # <class '__main__.Meta2'>
print Bar2.__metaclass__ # <class '__main__.Meta2'>
print Foo1.__class__ # <class '__main__.Meta1'>
print Bar1.__class__ # <class '__main__.Meta1'>
print Foo2.__class__ # <type 'type'>
print Bar2.__class__ # <type 'type'>
总结:
__metaclass__
和__class__
都将从基础class继承。
Meta2
定义的创建行为将用于Foo2
,虽然Foo2.__class__
实际上是type
.
Bar2
中的__metaclass__
属性为Meta2
,但Bar2
的创建行为不受影响。换句话说,Bar2
使用 type
作为其 "real" metaclass 而不是 Meta2
.
这些观察结果使 __metaclass__
的继承机制对我来说有点模糊。
我的猜测是:
当直接将一个class(例如Meta1
)赋给另一个class'Foo1'的__metaclass__
属性时,就是__metaclass__
属性生效。
当subclass定义时没有显式设置__metaclass__
属性。 base class 的 __class__
属性而不是 __metaclass__
属性将决定 subclass 的 "real" metaclass。
我的猜测是否正确? Python如何处理metaclass的继承?
你在猜测很多,而 Python 的极简主义和 "Special cases aren't special enough to break the rules." 指令,比那更容易理解。
在 Python2 中,class 主体中的 __metaclass__
属性在 class 创建时用于调用 "class" class 会。通常它是名为 type
的 class。澄清一下,那一刻是在解析器解析了 class 主体之后,在编译器将其编译为代码对象之后,并且在程序 运行 时实际上是 运行 之后,并且仅当 __metaclass__
在 class 正文中明确提供时。
所以让我们检查一下情况,例如:
class A(object):
__metaclass__ = MetaA
class B(A):
pass
A
在其主体中有 __metaclass__
- MetaA
被调用而不是 type
以使其成为 "class object"。
B
的正文中没有 __metaclass__
。创建后,如果您只是尝试访问 __metaclass__
属性,它和其他属性一样是一个属性,它将是可见的,因为 Python 将从 superclass A
.如果您选中 A.__dict__
,您将看到 __metaclass__
,如果您选中 B.__dict__
,则不会。
这个 A.__metaclass__
属性在创建 B 时 根本没有使用 。如果在声明 B
之前在 A
中更改它,仍将使用与 A
相同的元 class - 因为 Python 确实使用父 [=] 的类型99=] 作为 metaclass 在没有显式声明 __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: <class '__main__.M'>, metaclass_attr: <class '__main__.M'>, metaclass_in_dict: <class '__main__.M'>, type: <class '__main__.M'>
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: <class '__main__.M'>, metaclass_attr: <class '__main__.M'>, metaclass_in_dict: None, type: <class '__main__.M'>
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: <class '__main__.M'>, metaclass_attr: <type 'type'>, metaclass_in_dict: None, type: <class '__main__.M'>
此外,如果您尝试通过调用 type
而不是使用带有 class
语句的主体来创建 class,__metaclass__
也只是普通属性:
In [11]: D = type("D", (object,), {"__metaclass__": M})
In [12]: type(D)
type
到目前为止的总结: Python 2 中的 __metaclass__
属性只有在显式放置在 class 正文中时才是特殊的声明,作为 class
块语句执行的一部分。是普通属性,后面没有特殊属性。
Python3 都摆脱了这个奇怪的“__metaclass__
属性现在不好”,并允许通过更改语法以指定元来进一步自定义 class 主体class是的。 (就像在 class
语句本身上声明为“metaclass
命名参数”)
现在,对于引起您怀疑的第二部分:如果在元 class 的 __new__
方法中调用 type
而不是 type.__new__
,那么Python 不可能 "know" type
是从派生元 class 调用的。当您调用 type.__new__
时,您将 cls
属性作为第一个参数传递给您的 metaclass 的 __new__
本身是在 运行 时间传递的:即是什么将结果 class 标记为 type
的子 class 的实例。 这就像继承适用于 Python 中的任何其他 class - 所以 "no special behaviors" 此处:
所以,找出不同之处:
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(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)
(注意 metaclass __new__
方法的第一个参数是 metaclass 本身,因此命名为 metacls
比 cls
更恰当,如你的代码,还有很多代码 "in the wild")
在这个著名的 answer 中解释了 metaclass 在 Python 中。它提到 __metaclass__
属性不会被继承。
但事实上,我试过 Python:
class Meta1(type):
def __new__(cls, clsname, bases, dct):
print "Using Meta1"
return type.__new__(cls, clsname, bases, dct)
# "Using Meta1" printed
class Foo1:
__metaclass__ = Meta1
# "Using Meta1" printed
class Bar1(Foo1):
pass
正如预期的那样,Foo
和 Bar
都使用 Meta1
作为 metaclass 并按预期打印字符串。
但在下面的示例中,当返回 type(...)
而不是 type.__new__(...)
时,metaclass 不再被继承:
class Meta2(type):
def __new__(cls, clsname, bases, dct):
print "Using Meta2"
return type(clsname, bases, dct)
# "Using Meta2" printed
class Foo2:
__metaclass__ = Meta2
# Nothing printed
class Bar2(Foo2):
pass
检查 __metaclass__
和 __class__
属性,我可以看到:
print Foo1.__metaclass__ # <class '__main__.Meta1'>
print Bar1.__metaclass__ # <class '__main__.Meta1'>
print Foo2.__metaclass__ # <class '__main__.Meta2'>
print Bar2.__metaclass__ # <class '__main__.Meta2'>
print Foo1.__class__ # <class '__main__.Meta1'>
print Bar1.__class__ # <class '__main__.Meta1'>
print Foo2.__class__ # <type 'type'>
print Bar2.__class__ # <type 'type'>
总结:
__metaclass__
和__class__
都将从基础class继承。Meta2
定义的创建行为将用于Foo2
,虽然Foo2.__class__
实际上是type
.Bar2
中的__metaclass__
属性为Meta2
,但Bar2
的创建行为不受影响。换句话说,Bar2
使用type
作为其 "real" metaclass 而不是Meta2
.
这些观察结果使 __metaclass__
的继承机制对我来说有点模糊。
我的猜测是:
当直接将一个class(例如
Meta1
)赋给另一个class'Foo1'的__metaclass__
属性时,就是__metaclass__
属性生效。当subclass定义时没有显式设置
__metaclass__
属性。 base class 的__class__
属性而不是__metaclass__
属性将决定 subclass 的 "real" metaclass。
我的猜测是否正确? Python如何处理metaclass的继承?
你在猜测很多,而 Python 的极简主义和 "Special cases aren't special enough to break the rules." 指令,比那更容易理解。
在 Python2 中,class 主体中的 __metaclass__
属性在 class 创建时用于调用 "class" class 会。通常它是名为 type
的 class。澄清一下,那一刻是在解析器解析了 class 主体之后,在编译器将其编译为代码对象之后,并且在程序 运行 时实际上是 运行 之后,并且仅当 __metaclass__
在 class 正文中明确提供时。
所以让我们检查一下情况,例如:
class A(object):
__metaclass__ = MetaA
class B(A):
pass
A
在其主体中有 __metaclass__
- MetaA
被调用而不是 type
以使其成为 "class object"。
B
的正文中没有 __metaclass__
。创建后,如果您只是尝试访问 __metaclass__
属性,它和其他属性一样是一个属性,它将是可见的,因为 Python 将从 superclass A
.如果您选中 A.__dict__
,您将看到 __metaclass__
,如果您选中 B.__dict__
,则不会。
这个 A.__metaclass__
属性在创建 B 时 根本没有使用 。如果在声明 B
之前在 A
中更改它,仍将使用与 A
相同的元 class - 因为 Python 确实使用父 [=] 的类型99=] 作为 metaclass 在没有显式声明 __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: <class '__main__.M'>, metaclass_attr: <class '__main__.M'>, metaclass_in_dict: <class '__main__.M'>, type: <class '__main__.M'>
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: <class '__main__.M'>, metaclass_attr: <class '__main__.M'>, metaclass_in_dict: None, type: <class '__main__.M'>
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: <class '__main__.M'>, metaclass_attr: <type 'type'>, metaclass_in_dict: None, type: <class '__main__.M'>
此外,如果您尝试通过调用 type
而不是使用带有 class
语句的主体来创建 class,__metaclass__
也只是普通属性:
In [11]: D = type("D", (object,), {"__metaclass__": M})
In [12]: type(D)
type
到目前为止的总结: Python 2 中的 __metaclass__
属性只有在显式放置在 class 正文中时才是特殊的声明,作为 class
块语句执行的一部分。是普通属性,后面没有特殊属性。
Python3 都摆脱了这个奇怪的“__metaclass__
属性现在不好”,并允许通过更改语法以指定元来进一步自定义 class 主体class是的。 (就像在 class
语句本身上声明为“metaclass
命名参数”)
现在,对于引起您怀疑的第二部分:如果在元 class 的 __new__
方法中调用 type
而不是 type.__new__
,那么Python 不可能 "know" type
是从派生元 class 调用的。当您调用 type.__new__
时,您将 cls
属性作为第一个参数传递给您的 metaclass 的 __new__
本身是在 运行 时间传递的:即是什么将结果 class 标记为 type
的子 class 的实例。 这就像继承适用于 Python 中的任何其他 class - 所以 "no special behaviors" 此处:
所以,找出不同之处:
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(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)
(注意 metaclass __new__
方法的第一个参数是 metaclass 本身,因此命名为 metacls
比 cls
更恰当,如你的代码,还有很多代码 "in the wild")