【问题标题】:Why does calling super() in parent class __init__() change the subclass __init__() behavior?为什么在父类 __init__() 中调用 super() 会改变子类 __init__() 的行为?
【发布时间】:2013-06-20 14:14:45
【问题描述】:

我一直试图了解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() 的重点:确保以正确的顺序调用层次结构中的所有类。
  • @LennartRegebro 我现在意识到这一点,因为我已经阅读了@BrenBarn 的答案和这篇文章rhettinger.wordpress.com/2011/05/26/super-considered-super 我很困惑,因为我认为super() 会无条件地调用父类(在AB 的情况是 object)。令我惊讶的是,由于 MRO,A.__init__() 中的 super() 调用实际上调用了 B.__init__()(然后调用 object.__init__())。

标签: python multiple-inheritance superclass super method-resolution-order


【解决方案1】:

你的错误在于你的评论:

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

这不是对object.__init__ 的调用。将类 C 和实例 self 都传递给 super 的原因是它知道接下来要调用什么,而不仅仅是基于类的超类,而是基于实例的 MRO。本质上,super(C, self).__init__ 的意思是“在自己的 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。我从原始问题中删除了不正确的 cmets 并重新排序了对 super 的调用,以便它正确打印(这样我就不会无意中混淆后来遇到这个问题的人)。随时更新您的答案以匹配。
猜你喜欢
  • 2020-11-06
  • 1970-01-01
  • 2010-11-26
  • 1970-01-01
  • 1970-01-01
  • 2015-02-18
  • 1970-01-01
  • 2016-11-29
  • 1970-01-01
相关资源
最近更新 更多