首先,Python 3 中的super() 形式实际上与super(<CurrentClass>, self) 相同,其中Python 编译器provides enough information for super() to determine what the correct class to use is。所以在E.foo()中,super().foo()可以读作super(E, self).foo()。
要了解发生了什么,您需要查看class.__mro__ attribute:
此属性是在方法解析期间查找基类时考虑的类元组。
正是这个元组向您展示了 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 索引之后的下一个索引开始 .
- 如果以这种方式找到的属性是descriptor object,则将以这种方式找到的属性绑定到
self。函数是描述符,绑定产生绑定的方法,这就是 Python 在调用方法时传递self 引用的方式。
表示为忽略边缘情况和super() 的其他用途的简化 Python 代码,如下所示:
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') 被执行,导致 foo in E
-
super().foo() 被执行,实际上与super(E, self).foo() 相同。
- MRO 被搜索,从下一个索引开始过去
E,所以在D(没有foo 属性),移动到B(没有foo 属性),然后是 C(找到属性)。返回C.foo,绑定到self。
-
C.foo(self) 被调用,导致 foo fo C
-
super(B, self).foo() 被执行。
- MRO 被搜索,从下一个索引过去
B 开始,所以在C(找到属性)。返回C.foo,绑定到self。
-
C.foo(self) 被调用,导致 foo fo C
-
super(C, self).foo() 被执行。
- MRO 被搜索,从下一个索引过去
C 开始,所以在A(找到属性)。返回A.foo,绑定到self。
-
A.foo(self) 被调用,导致 foo of A