Python多重继承和super()

Python多重继承和super(),python,inheritance,constructor,multiple-inheritance,Python,Inheritance,Constructor,Multiple Inheritance,我试图理解Python中多重继承(即)是如何工作的。下面的经典玩具示例给出的结果与我的直觉相反 我特别注意到以下几点: 当按原样运行时(即A和AA都有super调用Base,则输出为: 当仅注释掉标记为(1)的行时(即在A中不调用Base构造函数),则输出为 当仅注释掉标记为(2)的行时(即,AA中没有调用Base构造函数),则输出为 当标记为(1)和(2)的两行都被注释掉时,输出为 我的问题是: 在案例1中,在显式调用Base构造函数之前,A的构造函数中的执行似乎被中断,跳转到AA构造函数(

我试图理解Python中多重继承(即)是如何工作的。下面的经典玩具示例给出的结果与我的直觉相反

我特别注意到以下几点:

  • 当按原样运行时(即
    A
    AA
    都有
    super
    调用
    Base
    ,则输出为:
  • 当仅注释掉标记为
    (1)
    的行时(即在
    A
    中不调用
    Base
    构造函数),则输出为
  • 当仅注释掉标记为
    (2)
    的行时(即,
    AA
    中没有调用
    Base
    构造函数),则输出为
  • 当标记为
    (1)
    (2)
    的两行都被注释掉时,输出为
  • 我的问题是:

    • 在案例1中,在显式调用
      Base
      构造函数之前,
      A
      的构造函数中的执行似乎被中断,跳转到
      AA
      构造函数(好像
      AA
      将派生自
      A
      ),然后下降到
      Base
      ,然后返回。会发生这种情况吗?(我理解MRO
      B->A->AA->Base
      来自C3要求在父类之前调用子类。)
    • 在案例2中,为什么从未调用
      AA
      的构造函数,尽管在案例3中调用了它
    • 在案例2和案例3中,尽管在
      AA
      (案例2)/
      A
      (案例3)的构造函数中进行了显式调用,但为什么没有调用
      Base
      构造函数(在示例1中,它是按我的预期进行调用的)
    以下是这些课程的MRO:

    • B
      (,)
    • A
      (,)
    • 类别
      AA
      (,)
    • Base
      (,)
    代码:

    !/usr/bin/env python
    #使用Python2.7
    类基(对象):
    定义初始化(自):
    打印“”,
    超级(基本,自我)。\uuuuu初始
    打印“”,
    A类(基本类):
    定义初始化(自):
    打印“”,
    超级(A,自我)。(1)
    打印“”,
    AA级(基本):
    定义初始化(自):
    打印“”,
    超级(AA,self)。uuuu初始(2)
    打印“”,
    B类(A、AA):
    定义初始化(自):
    打印“”,
    super(B,self)。\uuuuu init\uuuuuuu()
    打印“”,
    如果uuuu name uuuuuu='\uuuuuuu main\uuuuuuu':
    obj=B()
    
    来自您链接的维基百科文章:

    Python使用C3线性化算法创建一个类列表。该算法强制执行两个约束:子类在其父类之前,如果一个类从多个类继承,则它们将保持在基类元组中指定的顺序(然而,在这种情况下,继承图中较高的一些类可能位于图[8]中较低的类之前。)因此,方法解析顺序为:D,B,C,A


    因此在这种情况下(即,在B的继承结构中),A的super是AA。

    MRO是
    B
    A
    AA
    Base
    。这意味着
    super(A,self)的值
    self
    B
    时,对象是
    AA
    类的代理。如果构建了
    a
    对象,则相同的
    super
    调用将返回
    Base
    类的代理

    所有让你困惑的行为都直接源于此

    在A对象中,
    super(A,self).\uuu init\uuuuuuuuuuuuuuuuuuuuuuuuuu()调用将调用
    Base.\uuuuu init\uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuself)
    ,但

    编辑以在评论中添加问题的答案:

    不,您找不到super解析的类,原因很简单,不同的属性可以解析为不同的类

    例如,在您的代码中,给出
    class AA
    方法
    foo
    。现在:

    b = B()
    super(B,b).__init__() # calls A.__init__
    super(B,b).foo() # calls AA.foo
    

    super
    函数不只是查找MRO链中的下一个类,它还查找具有所需属性的下一个类。这就是为什么它必须返回代理对象,而不仅仅是返回类。

    您不应该将
    super
    视为对下一个“up”的函数调用相反,当正确使用时,
    super
    将确保以该顺序调用MRO中的所有函数。但为了实现该top,需要在该链的每个段中进行super调用

    因此,如果您在
    A
    AA
    中删除超级调用,则链将中断。根据您删除的内容,链将在
    A
    AA
    处中断:

    • 不间断(完整MRO):
      B
      A
      AA
      Base
    • 案例1(在
      A
      中没有超级调用):
      B
      A
    • 案例2(在
      AA
      中没有超级调用);
      B
      A
      AA
    因此,您应该记住始终在所有涉及的类型中使用super,以使其正常工作

    如果你想了解更多关于
    super
    ,你应该去今年的PyCon看看。它有很好的解释,还有一些简单易懂的例子(涉及真实的人!)

    引用他的话(抄写并强调我的话):

    Python中的
    super
    最大的问题是什么?这不是它的设计。我认为它的设计完美无缺,很漂亮,Guido在这方面做得非常出色

    问题是名称,它不应该被称为“super”。为什么不呢?答案是,如果你用任何其他语言学习super,它与Python都不一样

    […]它在其他语言中起什么作用?我
    b = B()
    super(B,b).__init__() # calls A.__init__
    super(B,b).foo() # calls AA.foo