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

时间:2019-05-08 12:48:25

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

1 个答案:

答案 0 :(得分:1)

问题是您的类没有合作定义。 AB都认为它引入了foo,因此不需要调用super().foo,因为每个人都认为这将是任何潜在的最后一个类。 MRO定义fooAB事实并非如此。

致电AB.foo时,它做的第一件事就是致电A.foo。但是,A.foo不使用super,因此链结束,并且B.foo从未被调用。

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

在您的A / B / AB情况下,您有几种选择:

  1. A继承BFooBase,并让它们各自从super().foo()的实现中调用fooFooBase.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__A本身需要定义,则情况变得更加复杂;有关使B在协作继承设置中正常工作的更多建议,请参见链接的文章。