多重继承调用顺序
A B
| | | |
C D E F
| | | |
G H
|
I
user@ubuntu:~/Documents/Python/oop_python$ cat tt.py
class A:
def call_me(self):
print("A")
class C(A):
def call_me(self):
super().call_me()
print("C")
class D(A):
def call_me(self):
super().call_me()
print("D")
class B:
def call_me(self):
print("B")
class E(B):
def call_me(self):
super().call_me()
print("E")
class F(B):
def call_me(self):
super().call_me()
print("F")
class G(C, D, E):
def call_me(self):
super().call_me()
print("G")
class H(F):
def call_me(self):
super().call_me()
print("H")
class I(G, H):
def call_me(self):
super().call_me()
print("I")
user@ubuntu:~/Documents/Python/oop_python$ python3.2 -i tt.py
>>> i = I()
>>> i.call_me()
A
D
C
G
I
// updated based on comments from delnan
user@ubuntu:~/Documents/Python/oop_python$ cat tt.py
class BaseClass():
def call_me(self):
print("BaseClass")
pass
class A(BaseClass):
def call_me(self):
super().call_me()
print("A")
class C(A):
def call_me(self):
super().call_me()
print("C")
class D(A):
def call_me(self):
super().call_me()
print("D")
class B(BaseClass):
def call_me(self):
super().call_me()
print("B")
class E(B):
def call_me(self):
super().call_me()
print("E")
class F(B):
def call_me(self):
super().call_me()
print("F")
class G(C, D, E):
def call_me(self):
super().call_me()
print("G")
class H(F):
def call_me(self):
super().call_me()
print("H")
class I(G, H):
def call_me(self):
super().call_me()
print("I")
user@ubuntu:~/Documents/Python/oop_python$ python3.2 -i tt.py
>>> i = I()
>>> i.call_me()
BaseClass
B
F
H
E
A
D
C
G
I
问题> 为什么 B
、E
、F
没有被打印出来?
2 个回答
使用super的前提是这个类必须是新式类。
在Python中,super有两个使用场景:
第一个场景是单继承的类层次结构,super可以用来引用父类,而不需要明确地写出父类的名字。
第二个场景是支持在动态执行环境中的协作多继承。
所以,第二个场景符合你提到的第二个例子。在这种情况下,子类会进行一个广度优先遍历,确保每个重写的基类方法只被调用一次。你的继承树可以按照这个顺序(遵循广度优先遍历,从左到右)重写为:I G C D A E H F B BaseClass。
super方法的定义是:
Super(type[,object-or-type])
它会返回一个代理对象,这个对象会把方法调用转发给指定类型的父类或兄弟类。在Python 3.x中,你可以直接调用super(),这和super(currentClassName, self)是一样的。这会获取通过广度优先遍历生成的直接右侧的父类或兄弟类的代理。所以方法i.call_me()的调用顺序是:
I.call_me -> G.call_me -> C.call_me -> D.call_me -> A.call_me -> E.call_me -> H.call_me -> F.call_me -> B.call_me -> BaseClass.call_me
两个例子的区别
你问为什么第一个例子没有打印B、E、F,因为继承图不是“钻石图”,这意味着A.call_me和B.call_me是不同的方法。所以Python只选择其中一个继承分支。
希望这些对你有帮助。你也可以查看Python类文档获取更多细节。
一个常见的误解是,super() 会调用所有父类的方法。其实并不是这样,它只会调用其中一个。具体调用哪个,是由 super() 根据一些特定的规则自动决定的。有时候,它调用的甚至不是父类,而是兄弟类。不过,除非所有的类都使用 super(),否则不能保证所有的方法都会被调用。
在这个例子中,A 和 B 并没有调用 super。如果你在 A 中添加 super,它会调用那些“缺失”的类,但如果你在 B 中添加 super,就会出错,因为在这个特定的情况下,B 会成为“最后一个”(或者说第一个,这取决于你的看法)类。
如果你想使用 super(),最好的办法是为 A 和 B 创建一个共同的基类,这个基类实现了 call_me 方法,但不调用 super()。(感谢 delnan 提出的建议)。
不过,如果你了解你的类层次结构,你可以直接调用父类的方法,而不是使用 super()。需要注意的是,在上面的例子中,这并不意味着每个类都必须直接调用它的每个父类。这在你作为程序员无法完全控制类层次结构的情况下并不实用,比如当你编写库或混合类时。这时候你就得使用 super()。