多重继承中的 Python '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,并让它们中的每一个都从其实现中调用foosuper().foo()FooBase.foo本身不调用super.

  2. 与其直接从AB继承AB,不如让它从围绕其中一个或两个包装器继承,其中包装器正确实现了协作继承。(参见 Python 的 super() 被认为是 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__正常工作的更多建议,请参阅链接的文章。

最新更新