多重继承调用顺序


 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

问题>为什么不打印BEF

//            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

一个常见的误解是 super(( 会调用所有超类方法。不会的。它只会调用其中一个。哪一个是由super((根据一些特定的规则自动计算的。有时它调用的那个不是真正的超类,而是兄弟姐妹。但是不能保证所有类都会被调用,除非所有类都使用 super((。

在这种情况下,A 和 B 不调用 super。如果你把它添加到A,它实际上会调用"缺失"类,但如果你把它添加到B,你会得到一个错误,因为在这种特殊情况下,B最终将成为"最后一个"(或第一个,取决于你如何看待它(类。

如果要使用 super((,最好的解决方案是为 A 和 B 提供一个实现 call_me 但不调用 super(( 的公共基类。(感谢德尔南的建议(。

但是,如果您知道类层次结构,则可以直接调用超类方法,而不是使用 super((。请注意,在上述情况下,这并不意味着每个类都必须直接调用其每个基类。因此,这在你作为程序员不能完全控制类层次结构的情况下是没有用的,例如,如果你编写库或mixin类。然后你必须使用 super((。

使用 super 的前提是类必须是新样式

python中的super有两个用例:

  1. 具有单一继承的类层次结构,super可用于引用父类而无需显式命名它们

  2. 第二个用例是在动态执行环境中支持协作多重继承。

因此,第二种情况与您的第二个示例条件匹配。在这种情况下,子类将执行 bfs 遍历,以确保每个覆盖基类方法只调用一次。您的继承树可以按此顺序重写为衬里(遵循 bfs 遍历从左到右(I G C D A E H F B BaseClass。

超级方法的定义是:

超级(类型[,对象或类型](

它将返回一个代理对象,该对象将方法调用委托给类型的父类或同级类。在python 3.x中,你可以直接调用super((,它与super(currentClassName,self(相同。这将获得由 bfs 遍历生成的直接右超同级类的代理。因此,方法 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 类文档以获取更多详细信息

最新更新