我从 github 得到了以下关于 MRO 和 C3 的代码,我不太明白最后三行,以及 python3.x 中 super().foo()、super(B,self).foo() 和 super(C,self).foo() 之间的区别,代码如下:
class A(object):
def foo(self):
print('foo of A')
class B(A):
pass
class C(A):
def foo(self):
print('foo fo C')
class D(B, C):
pass
class E(D):
def foo(self):
print('foo in E')
super().foo()
super(B, self).foo()
super(C, self).foo()
if __name__ == '__main__':
d = D()
d.foo()
e = E()
e.foo()
预期和实际结果如下:
foo fo C
foo in E
foo fo C
foo fo C
foo of A
首先,Python 3 中super()
的形式实际上与super(<CurrentClass>, self)
相同,其中 Python 编译器为super()
提供了足够的信息来确定要使用的正确类是什么。所以在E.foo()
,super().foo()
可以读作super(E, self).foo()
。
要了解发生了什么,您需要查看class.__mro__
属性:
此属性是在方法解析期间查找基类时考虑的类元组。
正是这个元组向您展示了任何给定类层次结构的 C3方法解析顺序。对于您的类E
,该顺序为:
>>> E.__mro__
(<class '__main__.E'>, <class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.A'>, <class 'object'>)
>>> for cls in E.__mro__: # print out just the names, for easier readability.
... print(cls.__name__)
...
E
D
B
C
A
object
super()
对象的所有内容都基于该有序类序列。电话会议
super(SomeClass, self).foo()
结果是以下一系列步骤:
super()
对象检索self.__mro__
元组。super()
在该元组中查找SomeClass
类的索引。- 访问
super()
对象上的foo
属性会触发对在 MRO 上具有foo
属性的类的搜索,从SomeClass
索引之后的下一个索引开始。
如果以这种方式找到的属性 - 是描述符对象,则将以这种方式找到的属性绑定到
self
.函数是描述符,绑定生成绑定方法,这就是 Python 在调用方法时传入self
引用的方式。
表示为简化的Python代码,忽略边缘情况和super()
的其他用途,如下所示:
class Super:
def __init__(self, type_, obj_or_type):
self.mro = obj_or_type.__mro__
self.idx = self.mro.index(type_) + 1
self.obj_or_type = obj_or_type
def __getattr__(self, name):
for cls in self.mro[self.idx:]:
attrs = vars(cls)
if name in attrs:
result = attrs[name]
if hasattr(result, '__get__'):
result = result.__get__(obj_or_type, type(self.obj_or_type))
return result
raise AttributeError(name)
结合这两个信息,您可以看到当您调用e.foo()
时会发生什么:
print('foo in E')
被执行,导致E 中的 foo- 执行
super().foo()
,实际上与super(E, self).foo()
相同。- 搜索 MRO,从过去
E
的下一个索引开始,因此从D
(无foo
属性)开始,移动到B
(无foo
属性),然后C
(找到属性)。 返回C.foo
,绑定到self
。 - 调用
C.foo(self)
,导致foo fo C
- 搜索 MRO,从过去
super(B, self).foo()
被执行。- 搜索 MRO,从过去
B
的下一个索引开始,因此从C
(找到属性)开始。C.foo
返回,绑定到self
. C.foo(self)
被调用,导致foo fo C
- 搜索 MRO,从过去
- 执行
super(C, self).foo()
。- 从过去
C
的下一个索引开始搜索 MRO,因此从A
(找到属性)开始。 返回A.foo
,绑定到self
。 - 调用
A.foo(self)
,导致A 的 foo
- 从过去