我对以下代码示例感到非常困惑:
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
类。
我真的很困惑:
- 在第 7 行中,似乎
super(Meta_1, cls).__call__(*a, **kw)
最终会导致type.__call__
. 但是,据我所知,super(arg1, arg2)
将研究第二个输入参数的 MRO 以找到第一个输入参数,并将下一个类返回给它。但是在我的代码的第 6 行和第 7 行中,第二个参数(Car
) 的 MRO 不包含第一个输入参数(Meta_1
),您在 MRO 中找不到Car
Meta_1
。那么super(Meta_1, cos)
为什么要带我们调用type.__call__
呢?
2. 如果super(Meta_1, cls).__self__
是Car
类,那么第 7 行意味着它被称为Car
__call__
? 但是打电话给Car
班首先把我们带到了1号线,对吧?那不是循环吗?
你混淆了一些概念。第一个是将元类与类继承层次结构混淆。
这两件事都是ortogonal - 查看Car
的MRO将显示该类的继承树,其中不包括元类。换句话说,任何Meta_1
都不应以任何方式位于 MRO(或继承树)中。
元类是类的类型 - 也就是说,它具有创建类对象本身的模板和方法。因此,它具有构建类 MRO 本身的"机制",并调用类的__new__
和__init__
(以及__init_subclass__
和初始化调用其__set_name__
的描述符)。
因此,调用类对象,就像在 Python 中调用任何实例一样,将在它的类__call__
方法中运行代码。在类的情况下,碰巧"调用"类是创建新实例的方法 - 这是元类的__call__
。你误解的另一件事是super()
对象。Super()
实际上不是超类,也不是超类的实例 - 它是一个代理对象,它将任何属性检索或方法调用中继到正确超类上的方法和属性。作为super()
用来充当代理的机制的一部分,是将调用它的实例作为自己的__self__
属性。换句话说,__self__
属性是调用返回的(代理)对象super()
普通属性 - 它是从第二个参数中选取的,或者在 Python 3 中自动选择的 - 当super
对象用作代理时,它在内部使用,以获得就好像它正在访问该实例的"超类"上的属性或方法一样。(在__self__
中注释的实例)。
当你在元类中使用super()
时,代理的类是元类的超类,它是type
,而不是Car的超类,object
。
所以你的第二个问题:
- 如果
super(Meta_1, cls).__self__
是 Car 类,那么第 7 行表示它被称为 Car 的__call__
?但是打电话给汽车 上课首先把我们带到了1号线,对吧?那不是 圈?
如上所述,来自元类__call__
的super()
调用将调用type.__call__
,并将类Car
作为其cls
参数。反过来,该方法将运行Car.__new__
并Car.__init__
作为实例化类的正常过程。
重要的是要注意哪些值被用作要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_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__
的调用,以便创建一个Meta_1.__call__
可以返回的cls
实例。
这是Michael Ekoka原始帖子的一个很好的答案,我的示例代码来自其中: 使用元类的__call__方法而不是__new__?
基本上,我需要更好地了解super()
的工作原理。
报价:
super
确实会使用cls
来找到MRO,但不是人们可能想象的那样。我猜你认为它会做一些像cls.__mro__
一样直接的事情并找到Meta_1
.不是这样,这就是您通过这样做解决的Class_1
的 MRO,一个不同的、不相关的 MRO,Meta_1
不是它的一部分(Class_1
不会继承自Meta_1
)。cls
即使拥有__mro__
财产也只是一个意外,因为它是一个阶级。相反,super
将查找cls
的类(在我们的例子中是元类),即Meta_1
,然后将从那里查找 MRO(即Meta_1.__mro__
)。