为什么在父类__init__()中调用super()会改变子类__init__()的行为?

4
我一直在尝试理解在多重继承的情况下,super() 的行为。我对于为什么在test2.py中父类中的super()调用会导致两个父类的__init__()都被调用感到困惑。 test1.py
#!/usr/bin/env python

class A(object):

    def __init__(self):
        self.A = "A"
        print self.A

class B(object):

    def __init__(self):
        self.B = "B"
        print self.B

class C(A, B):

    def __init__(self):
        self.C = "C"
        print self.C
        super(C, self).__init__()

if __name__ == '__main__':
    print "Without super() in parent __init__():"
    c = C()
    print c.__dict__
    print C.__mro__

输出:

$ ./test.py 
Without super() in parent __init__():
C
A
{'A': 'A', 'C': 'C'}
(<class '__main__.C'>, <class '__main__.A'>, <class '__main__.B'>, <type 'object'>)

test2.py

#!/usr/bin/env python

class A(object):

    def __init__(self):
        self.A = "A"
        print self.A
        super(A, self).__init__()

class B(object):

    def __init__(self):
        self.B = "B"
        print self.B
        super(B, self).__init__()

class C(A, B):

    def __init__(self):
        self.C = "C"
        print self.C
        super(C, self).__init__()

if __name__ == '__main__':
    print "With super() in parent __init__():"
    c = C()
    print c.__dict__
    print C.__mro__

生成:

$ ./test2.py 
With super() in parent __init__():
C
A
B
{'A': 'A', 'C': 'C', 'B': 'B'}
(<class '__main__.C'>, <class '__main__.A'>, <class '__main__.B'>, <type 'object'>)

“我很困惑为什么test2.py中父类中对super()的调用会导致两个父类的__init__()都被调用?” - 因为这就是super()的全部意义:确保按正确顺序调用层次结构中的所有类。 - Lennart Regebro
@LennartRegebro 我现在意识到了,因为我读了@BrenBarn的答案和这篇文章https://rhettinger.wordpress.com/2011/05/26/super-considered-super/。我感到困惑,因为我认为`super()`会无条件地调用父类(在`A`和`B`的情况下是`object`)。令我惊讶的是,我发现`A.__init__()`中的`super()`调用实际上调用了`B.__init__()`(然后调用了`object.__init__()`),这是由于MRO所致。 - cdwilson
1个回答

6

你的错误在于你的注释:

super(C, self).__init__()  <-- call to object.__init__()

这不是对object.__init__的调用。将类C和实例self都传递给super的原因在于它知道接下来要调用什么,不仅基于类的超类,而且基于实例的MRO。基本上,super(C, self).__init__的意思是“在self的MRO中调用C之后的类的__init__”。
这就是super的全部目的,它允许协作继承,其中一个类只需调用super表示“将控制权传递给MRO中的下一个类”,而无需在类定义时知道是哪个类。
因此,当您调用super(C, self).__init__时,会调用A.__init__,因为A是MRO中C之后的下一个类。然后,当A调用super(A, self).__init__时,会调用B.__init__,因为B是MRO中A之后的下一个类。
(请注意,您的消息以相反的顺序打印 --- B,A,C --- 因为您在调用超类方法之后才打印每条消息。因此,在执行到达B.__init__之前,第一条消息不会被打印出来,其他消息则是在继承树下降的过程中打印出来的。)

感谢您提供的有用答案!那个评论是无意中放在了C类中(我本来想把它放在A类中,现在我意识到这也是错误的...)。最初我很困惑,因为我不理解实例的MRO,认为A和B都会始终调用object.init()而不是遵循MRO。我从原始问题中删除了不正确的注释,并重新排序了对super的调用,以便正确打印(这样我就不会无意中混淆后来者)。请随意更新您的答案以匹配。 - cdwilson

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接