以下代码示例让我很困惑:
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__()
结果显示,第4行的 cls
是Car
类,其MRO列表是:
[<class '__main__.Car'>, <class 'object'>]
但是,第6行显示 super(Meta_1, cls).__self__
也是Car
类。
我真的很困惑:
super(Meta_1, cls).__call__(*a, **kw)
最终导致了type.__call__
。
但是,据我所知,super(arg1, arg2)
将调查第二个输入参数的MRO,以找到第一个输入参数,并将下一个类返回给它。但是在我的代码的第6和第7行中,第二个参数(Car
)的MRO不包含第一个输入参数(Meta_1
),因此您无法在MRO中找到Meta_1
Car
。那么super(Meta_1, cos)
为什么要带我们调用type.__call__
呢? 2。如果 super(Meta_1, cls).__self__
是Car
类,那么第7行意味着正在调用的是Car
的{{1}}?但是,调用__call__
类将我们带到了第一行,对吗?那不是循环吗?
答案 0 :(得分:1)
您正在混淆一些概念。首先是将元类与类继承层次结构混淆。
两者都是正交的-查看Car
的mro将为您显示该类的继承树,并且该继承树不包括元类。换句话说,无论如何,任何Meta_1
都不应位于MRO(或继承树)中。
元类是类的类型-也就是说,它具有创建类对象本身的模板和方法。因此,它具有“机制”来构建类MRO本身,并调用类的__new__
和__init__
(以及__init_subclass__
并初始化描述符,调用其{{1} }。
因此,像调用Python中的任何实例一样,调用类对象将在其类__set_name__
方法中运行代码。对于类而言,碰巧“调用”该类是创建新实例的方式-这就是元类的__call__
。
您误解的另一件事是__call__
对象。 super()
实际上不是超类,也不是超类的实例-而是代理对象,它将把任何属性检索或方法调用中继到适当的超类上的方法和属性。作为机制Super()
的一部分,它可以用作代理,并具有 instance (在实例中称为其自己的super()
属性)。换句话说,__self__
属性是__self__
调用返回的(代理)对象上的普通属性-从第二个参数中选择,或者在Python 3中自动获取-并且在以下情况下在内部使用super()
对象用作代理来获取行为,就好像它正在访问该实例的“超类”上的属性或方法一样。 (在super
中注释的实例)。
当您在元类中使用__self__
时,代理的类是元类的超类,即super()
,而不是Car的超类,type
。
第二个问题:
- 如果
object
是Car类,那么第7行意味着正在调用的是汽车的super(Meta_1, cls).__self__
?但是打电话给汽车 上课首先把我们带到了第一行,对吧?那不是一个 循环吗?
如上所述,从元类'__call__
进行的super()
调用将调用__call__
,它将获得类type.__call__
作为其Car
参数。该方法将依次运行cls
和Car.__new__
作为实例化类的常规过程。
答案 1 :(得分:1)
请务必注意将什么值用作super
的每个参数。 super
的主要目的是根据某种方法解析顺序(MRO)执行属性查找。第二个参数确定要使用哪个MRO;首先确定开始的位置。
MRO始终由 class 定义;在对实例执行方法解析时,我们使用该实例为类型的类的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_1.__call__
的某个对象。这意味着我们将使用type(cls)
提供的MRO,并使用在之后 Meta_1
找到的第一个类,该类提供了所需的方法。 (在第一次调用中,__self__
是代理对象本身的属性,而不是其代理super
返回的类的属性或方法。)
运行代码时,您看到cls
已绑定到Car
类型的对象。这是因为Car()
是由type(Car).__call__()
实现的;由于Car
使用Meta_1
作为其元类,所以type(Car)
是Meta_1
。
cls.mro()
无关紧要,因为这是cls
中实例所使用的MRO。
Meta_1
本身的MRO可以通过
>>> Meta_1.mro(Meta_1)
[<class '__main__.Meta_1'>, <class 'type'>, <class 'object'>]
({mro
是type
类的实例方法,因此需要看似冗余的type
实例作为参数。请记住,cls.mro()
是等效的到type(cls).mro(cls)
。)
所以第7行是对type.__call__
的调用,以便创建cls
可以返回的Meta_1.__call__
的实例。
答案 2 :(得分:0)
这是Mathias R. Jessen的原始帖子的一个很好的答案,我的示例代码来自: Michael Ekoka
基本上,我需要对super()
的工作方式有更好的了解。
引用:
super
的确会使用cls
查找MRO,但不是人们可能想的那样。我猜想您认为它会做与cls.__mro__
一样直接的事情并找到Meta_1
。并非如此,这是您要解决的Class_1
的MRO,一个不同的,不相关的MRO,而Meta_1
不是它的一部分(Class_1
并非继承自{ {1}})。 Meta_1
甚至拥有cls
属性,由于它是一个类,因此只是偶然。相反,__mro__
将查找super
的类(在我们的情况下为元类),即cls
,然后从那里查找MRO(即Meta_1
)。