Lif*_*ang 11 python inheritance 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
Run Code Online (Sandbox Code Playgroud)
正如预期的那样,无论是Foo
和Bar
使用Meta1
作为元类和打印字符串预期.
但是在下面的示例中,type(...)
返回的是when 而不是type.__new__(...)
,继承了元类:
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
Run Code Online (Sandbox Code Playgroud)
检查__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'>
Run Code Online (Sandbox Code Playgroud)
结论:
双方__metaclass__
并__class__
会从基类继承.
Meta2
将使用定义的创建行为Foo2
,尽管Foo2.__class__
实际上是type
.
该__metaclass__
在属性Bar2
是Meta2
,但创造的行为Bar2
不会受到影响.换句话说,Bar2
使用type
它作为"真正的"元类而不是Meta2
.
这些观察使得我的继承机制变得__metaclass__
模糊.
我的猜测是:
当直接将类(例如Meta1
)分配给__metaclass__
另一个类'Foo1'的__metaclass__
属性时,它就是生效的属性.
子类__metaclass__
在定义时未明确设置属性.该__class__
属性,而不是__metaclass__
基类的属性将决定子类的"真正的"元类.
我猜是正确的吗?Python如何处理元类的继承?
您正在猜测很多,而Python的极简主义和“特殊情况还不足以打破规则。” 指令,比这更容易理解。
在Python2中,__metaclass__
在类创建时使用类主体中的属性来调用该类将成为的“类”。通常,它是名为的类type
。为了明确起见,那一刻是在解析器解析了类主体之后,在编译器将其编译为代码对象之后,以及在程序运行时实际运行之后,并且仅__metaclass__
在该类主体中明确提供了该时刻。
因此,让我们检查一下这种情况下的情况:
class A(object):
__metaclass__ = MetaA
class B(A):
pass
Run Code Online (Sandbox Code Playgroud)
A
已经__metaclass__
在它的身上- MetaA
被称为代替type
,使之成为“类对象”。
B
没有__metaclass__
在它的身上。创建__metaclass__
属性后,如果您只是尝试访问该 属性,则该属性与其他属性一样将是可见的,因为Python将从超类获取它A
。如果你检查A.__dict__
,你会看到 __metaclass__
,如果你查B.__dict__
不知道。
创建B时根本不使用此A.__metaclass__
属性。如果在声明之前进行更改,则仍将使用与-相同的元类,因为Python确实在不使用显式声明的情况下将父类的类型用作元类。A
B
A
__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'>
Run Code Online (Sandbox Code Playgroud)
此外,如果您尝试通过调用来创建一个类,type
而不是使用带有class
声明的主体,那么这__metaclass__
也只是一个普通属性:
In [11]: D = type("D", (object,), {"__metaclass__": M})
In [12]: type(D)
type
Run Code Online (Sandbox Code Playgroud)
到目前为止,总结:__metaclass__
Python 2中的属性只有在将其显式放置在类主体声明中(作为class
block语句执行的一部分)时才是特殊的。这是普通属性,之后没有特殊属性。
Python3都摆脱了这种奇怪的“ __metaclass__
属性现在不好”,并允许通过更改语法以指定元类来进一步自定义类主体。(就像metaclass
在class
声明本身上声明它是一个“ 命名参数”一样)
现在,引起您怀疑的第二部分是:如果在__new__
元类的方法中调用type
而不是 type.__new__
,则无法type
从派生的元类中调用Python 。当您调用时type.__new__
,您会将运行时传递的cls
元类__new__
本身的属性作为第一个参数传递:这就是将结果类标记为的子类实例的原因type
。就像继承适用于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
Run Code Online (Sandbox Code Playgroud)
可以在交互式提示中看到:
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)
Run Code Online (Sandbox Code Playgroud)
(请注意,metaclass __new__
方法的第一个参数是metaclass本身,因此,metacls
比cls
在您的代码中和在许多“野蛮的”代码中更正确地命名)