多重继承调用顺序

时间:2012-01-08 17:04:02

标签: python python-3.x multiple-inheritance super

 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

2 个答案:

答案 0 :(得分:6)

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

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

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

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

答案 1 :(得分:1)

使用super的前提条件是类必须是 new-style

python中有两个用例:

  1. 具有单继承的类层次结构,super可用于引用父类而不明确命名它们

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

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

    super方法的定义是:

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

    它将返回一个代理对象,该方法将方法调用委托给父类或兄弟类的类型。在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 class doc了解更多详情