Python 调用super()时,元类如何处理MRO列表?
我真的被下面的代码示例弄糊涂了:Python 调用super()时,元类如何处理MRO列表?,python,super,metaclass,method-resolution-order,Python,Super,Metaclass,Method Resolution Order,我真的被下面的代码示例弄糊涂了: class Meta_1(type): def __call__(cls, *a, **kw): # line 1 print("entering Meta_1.__call__()") print(cls) # line 4 print(cls.mro()) # line 5
class Meta_1(type):
def __call__(cls, *a, **kw): # line 1
print("entering Meta_1.__call__()")
print(cls) # line 4
print(cls.mro()) # line 5
print(super(Meta_1, cls).__self__) # line 6
rv = super(Meta_1, cls).__call__(*a, **kw) # line 7
print("exiting Meta_1.__call__()")
return rv
class Car(object, metaclass=Meta_1):
def __new__(cls, *a, **kw):
print("Car.__new__()")
rv = super(Car, cls).__new__(cls, *a, **kw)
return rv
def __init__(self, *a, **kw):
print("Car.__init__()")
super(Car,self).__init__(*a, **kw)
if __name__ == '__main__':
c = Car()
此代码的打印消息为:
entering Meta_1.__call__()
<class '__main__.Car'> # line 4
[<class '__main__.Car'>, <class 'object'>] # line 5
<class '__main__.Car'> # line 6
Car.__new__()
Car.__init__()
exiting Meta_1.__call__()
输入Meta\u 1.\u调用
#第4行
[,]#第5行
#第6行
汽车。新的
汽车。初始
正在退出Meta_1.__调用__()
结果显示,4号线的cls
为Car
类,其MRO列表为:[,]
但是,第6行显示super(Meta_1,cls)。\uuuuu self\uuuuu
也是Car
类
我真的很困惑:
super(Meta\u 1,cls).\u调用
最终导致type.\u调用
。
但是,据我所知,super(arg1,arg2)
将研究第二个输入参数的MRO以找到第一个输入参数,并将下一个类返回给它。但是在我的代码的第6行和第7行中,第二个参数(Car
)的MRO不包含第一个输入参数(Meta\u 1
),您无法在Car
的MRO中找到Meta\u 1
。那么为什么super(Meta\u 1,cos)
会让我们调用type.\u调用\u
李>
二,。如果
super(Meta\u 1,cls)。\uuu self\uuuuu
是Car
类,那么第7行表示调用的是Car
?但是打电话给汽车
班首先把我们带到了1号线,对吗?这不是一个循环吗 您混淆了一些概念。第一个问题是将元类与类继承层次结构混淆
这两件事都是正交的——查看Car
的mro将向您显示该类的继承树,而这并不包括元类。换句话说,无论如何,没有Meta_1
不应该位于MRO(或继承树)中
元类是类的类型——也就是说,它具有创建类对象本身的模板和方法。因此,它有“机制”来构建类MRO本身,并调用类“\uuuuu new\uuuu
和\uuuu init\uuuu
(和\uuuu init\u子类\uuuuu
,并初始化调用其\uu集\uu name>的描述符)
因此,调用一个类对象,就像调用Python中的任何实例一样,将在它的class\uuuu call\uuu
方法中运行代码。在类的情况下,“调用”类是创建新实例的方式,而元类“\uuuuu call\uuu
”就是这样做的
您误解的另一件事是super()
对象Super()
实际上不是超类,也不是超类的实例-而是一个代理对象,它将任何属性检索或方法调用传递到适当超类上的方法和属性。作为super()
用作代理的机制的一部分,是将实例作为其自身的\uuuuuuuuuuuuuuuuuuuuuuu属性调用。换句话说,\uuu self\uuu
属性是super()
调用返回的(代理)对象上的普通属性-它从第二个参数中选取,或者在Python3中自动执行—当super
对象用作代理时,它会在内部使用,就像它正在访问该实例的“超类”上的属性或方法一样。(在\uuuu self\uuu
中注释的实例)
在元类内部使用super()
时,代理的类是元类的超类,即类型
,而不是Car的超类,对象
关于你的第二个问题:
如果super(Meta\u 1,cls)。\uuuuuu self\uuuu
是汽车类,那么第7行表示正在调用的是汽车的\uu调用\uuu
?但是给车打电话
第一节课把我们带到了一号线,对吗?那不是个好主意吗
环路
如上所述,元类的super()
调用将调用type.\uu调用,它将获得类Car
作为其cls
参数。该方法将依次运行Car.\uuuuu new.\uuuu
和Car.\uuuu init.\uuuu
作为实例化类的正常过程。重要的是要注意super
的每个参数都使用了哪些值。super
的主要目的是根据某种方法解析顺序(MRO)执行属性查找。第二个参数决定使用哪个MRO;第一个决定从哪里开始寻找
MRO始终由类定义;在对实例执行方法解析时,我们使用该实例所属类的MRO
在课堂上
class Meta_1(type):
def __call__(cls, *a, **kw): # line 1
print("entering Meta_1.__call__()")
print(cls) # line 4
print(cls.mro()) # line 5
print(super(Meta_1, cls).__self__) # line 6
rv = super(Meta_1, cls).__call__(*a, **kw) # line 7
print("exiting Meta_1.__call__()")
return rv
我们看到了super
的两种用法。两者的论点相同cls
是作为第一个参数传递给Meta\u 1.\u\u调用\u\u
的某个对象。这意味着我们将使用type(cls)
提供的MRO,并使用在Meta\u 1
之后找到的第一个类来提供所需的方法。(在第一个调用中,\uuuu self\uuu
是代理对象本身的属性,而不是其代理super
返回的类的属性或方法。)
运行代码时,您会看到cls
绑定到Car
type对象。这是因为Car()
是由type(Car)实现的;由于Car
使用Meta\u 1
作为其元类,type(Car)
是Meta\u 1
cls.mro()
Meta_1
本身的MRO可以通过
>>> Meta_1.mro(Meta_1)
[<class '__main__.Meta_1'>, <class 'type'>, <class 'object'>]
>>Meta_1.mro(Meta_1)
[, ]
(mro
是type
类的实例方法,因此需要type
的看似冗余的实例作为参数。请记住