使用文件 super5.py:
class A:
def m(self):
print("m of A called")
class B(A):
def m(self):
print("m of B called")
super().m()
class C(A):
def m(self):
print("m of C called")
super().m()
class D(B,C):
def m(self):
print("m of D called")
super().m()
Run Code Online (Sandbox Code Playgroud)
我们可以做到以下几点:
>>> from super5 import D
>>> x = D()
>>> x.m()
m of D called
m of B called
m of C called
m of A called
Run Code Online (Sandbox Code Playgroud)
对我来说,这没有意义,因为当我执行时x.m(),我希望会发生以下情况:
m的D被执行,因此"m of D called"被输出。super().m()执行第二行,它首先将我们带到mof B。mof B,"m of B called"是先输出,然后,mofA由于super.m()调用了mof B,被执行,然后"m of A called"输出。mofC的执行方式类似于 3。如您所见,我希望看到的是:
m of D called
m of B called
m of A called
m of C called
m of A called
Run Code Online (Sandbox Code Playgroud)
为什么我错了?python 是否以某种方式跟踪对super()特定超类的调用次数并将执行限制为 1?
不,Python 在一个特殊__mro__属性中跟踪所有超类(新型类中的方法解析顺序):
print(D.__mro__)
Run Code Online (Sandbox Code Playgroud)
你得到:
(<class 'D'>, <class 'B'>, <class 'C'>, <class 'A'>, <class 'object'>)
Run Code Online (Sandbox Code Playgroud)
因此,当您调用 时super,它会按顺序遵循此列表。
看到这个问题:mro() 是做什么的?.
一切都在“多重继承”一章的官方文档中进行了解释。
对于大多数目的,在最简单的情况下,您可以将搜索从父类继承的属性视为深度优先,从左到右,而不是在层次结构重叠的同一类中搜索两次。因此,如果在 DerivedClassName 中找不到属性,则在 Base1 中搜索它,然后(递归地)在 Base1 的基类中搜索,如果在那里找不到,则在 Base2 中搜索,依此类推。
事实上,它比这稍微复杂一些;方法解析顺序动态更改以支持对 super() 的协作调用。这种方法在其他一些多继承语言中称为 call-next-method,并且比单继承语言中的超级调用更强大。
动态排序是必要的,因为多重继承的所有情况都表现出一种或多种菱形关系(其中至少一个父类可以通过来自最底层类的多条路径访问)。例如,所有类都继承自对象,因此任何多重继承的情况都提供了多个到达对象的路径。为了防止基类被多次访问,动态算法以一种保留每个类中指定的从左到右顺序的方式线性化搜索顺序,只调用每个父类一次,并且是单调的(意味着一个类可以被子类化而不影响其父类的优先顺序)。综上所述,这些特性使得设计具有多重继承的可靠且可扩展的类成为可能。