python 'super' 在多重继承中 - 意外的代码输出

python 'super' in multiple inheritance - unexpected code output

我对 python OOP 比较陌生。 尽管我对 JAVA OOP 有一些经验并且我知道方法 'super' 的含义,但当我有多个继承时,我很难理解它在 python 中是如何工作的(没有存在于 JAVA)

在挖掘找到一些答案后,我读到根据继承图,对于每个 class python 制定了一个方法解析顺序 (MRO) 以确定以什么顺序查找一个实例方法。

我还看到 MRO 由 "old style" 或 "new style" 决定,具体取决于我的 python 版本。 我有 python 3.7,所以我使用 "new style" 方法。

如果我没理解错的话,每次重写一个方法调用'super',python就会转到之后[=]出现的class中的方法40=] MRO 中的当前 class。

的确,当我运行以下代码时:

class A(object):
    def go(self):
        print("go A go!")

class B(A):
    def go(self):
        super(B, self).go()
        print("go B go!")

class C(A):
    def go(self):
        super(C, self).go()
        print("go C go!")

class D(B,C):
    def go(self):
        super(D, self).go()
        print("go D go!")

if __name__ == "__main__":
    d = D()
    d.go()
    print(D.__mro__)

我得到了输出:

go A go!
go C go!
go B go!
go D go!
(<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.A'>, <class 'object'>)

到目前为止一切顺利,但是当我尝试 运行 以下代码时:

class A(object):
    def foo(self):
        print('this is A')

class B(object):
    def foo(self):
        print('this is B')

class AB(A,B):
    def foo(self):
        super(AB,self).foo()
        print('this is AB')

if __name__ == '__main__':
    x = AB()
    x.foo()
    print(AB.__mro__)

我得到了输出:

this is A
this is AB
(<class '__main__.AB'>, <class '__main__.A'>, <class '__main__.B'>, <class 'object'>)

而不是我预期的输出:

this is B
this is A
this is AB
(<class '__main__.AB'>, <class '__main__.A'>, <class '__main__.B'>, <class 'object'>)

所以显然我不明白发生了什么......

对于这种情况的任何解释,以及关于 python 如何确定 MRO(根据 "new style")的任何解释,将不胜感激!

问题是您的 classes 不是协同定义的。 AB都认为已经引入了foo,所以没必要调用super().foo,因为各自认为会是最后一个class ] 在任何潜在的 MRO 中定义 fooAB 证明事实并非如此。

当您调用 AB.foo 时,它做的第一件事就是调用 A.foo。但是,A.foo 不使用 super,因此链结束,并且 B.foo 永远不会被调用。

在正确设计的层次结构中,恰好 一个 class "introduces" 一个方法,负责通过不调用来终止链 super.任何其他想要成为层次结构一部分的 class 负责调用 super.

在你的A/B/AB情况下,你有几个选择:

  1. AB 都继承自 FooBase,并让它们从 [=13= 的实现中调用 super().foo() ]. FooBase.foo本身不调用super.

  2. 而不是 AB 直接继承自 AB,让它从围绕其中一个或两个的包装器继承,其中包装器正确实现合作遗产。 (有关详细信息,请参阅 Python's super() considered super!.。)

    例如,这里我们包装A并让B作为foo的基础:

    class A:
        def foo(self):
            print('this is A')
    
    
    class AWrapper:
        def __init__(self, **kwargs):
            super().__init__()
            self.a = A()
    
        def foo(self):
            super().foo()
            self.a.foo()
    
    
    class B(object):
        def foo(self):
            print('this is B')
    
    # Important: AWrapper must come first
    class AB(AWrapper, B):
        def foo(self):
            super().foo()
            print('this is AB')
    
    AB().foo()
    

请注意,如果 __init__ 本身需要在 AB 中定义,这将变得更加复杂;有关使 __init__ 在合作继承设置中正常工作的更多建议,请参阅链接文章。