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

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

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

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

我还读到 MRO 由“旧式”或“新式”决定,具体取决于我的 python 版本。 我有 python 3.7,所以我使用“新样式”方法。

如果我理解正确,每次重写一个方法并调用“super”时,python 都会转到 MRO 中当前类之后出现的类中的方法。

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

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(根据“新样式”),将不胜感激!

最佳答案

问题是你的类不是协同定义的。 AB都认为它引入了foo,因此不需要调用super().foo,因为每个人都认为这将是任何潜在 MRO 中定义 foo 的最后一个类。 AB 证明事实并非如此。

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

在正确设计的层次结构中,只有 一个 类“引入”了一个方法,并负责通过不调用 super 来终止链。任何其他想要成为层次结构一部分的类都负责调用 super

在您的 A/B/AB 案例中,您有几个选择:

  1. AB 都继承自 FooBase,并分别调用 super().foo () 来自其 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__ 在合作继承设置中正常工作的更多建议,请参阅链接文章。

关于多重继承中的 python 'super' - 意外的代码输出,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/56041198/

相关文章:

python - 删除位数组中前导 0 的索引

python - 应用条件格式(没有错误),但工作表不会更新,直到我手动转到 'Edit Rules'

c# - 需要抽象对象集合 (C#)

oop - 扩展 DAO 的范围

python - 为什么 Python 3 会输出\xe3,一个额外的字符?

python - 方法调用前检查条件

python - 简单数据操作 : R vs python

python - 如何查找并替换文本文件中的一行?

delphi - Delphi 是否有通用的 "Object Pool"实现?

python - 规范化列 : sum to 1