为什么 super() 继承了“错误”的类?

Flo*_*nne 2 python super diamond-problem

我正在查看钻石问题并得到一个问题:


class A:
    def __init__(self):
        print("This is class A")

class B(A):
    def __init__(self):
        print("This is class B")
        super().__init__()

class C(A):
    def __init__(self):
        print("This is class C")
        super().__init__()

class D(B, C):
    def __init__(self):
        print("This is class D")
        super().__init__()


i = D()

Run Code Online (Sandbox Code Playgroud)
This is class D
This is class B
This is class C
This is class A
Run Code Online (Sandbox Code Playgroud)

它按预期工作,这很好,但我想知道为什么super().__init__()inclass B不去class A而是 C 被调用。

如果一个类有一个 super() 并且它继承自一个父类,它应该去那里。

如果我在 B 上删除它,代码将不会到达 C 或 A。

我知道 MRO 以及它是如何按预期进行的:

>>> D.__mro__
(<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.A'>, <class 'object'>)
Run Code Online (Sandbox Code Playgroud)

但我不知道为什么。

比这段代码的非 super() 实现具有相同的 MRO 但 A 打印两次非常奇怪:

This is class D
This is class B
This is class C
This is class A
Run Code Online (Sandbox Code Playgroud)
(<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.A'>, <class 'object'>)
Run Code Online (Sandbox Code Playgroud)

这是相反的,我知道 MRO 是正确的,但奇怪的是实际执行并没有那样:

This is class D
This is class B
This is class A
This is class C
This is class A
Run Code Online (Sandbox Code Playgroud)

我想知道 super() 行为背后的逻辑是什么。

当在网上问这个问题时,几乎每个人都将我链接到这个:https : //rhettinger.wordpress.com/2011/05/26/super-thinked-super/但我真的不明白,他的解释似乎太技术和他的例子(我理解的少数几个)比解释这一点要复杂得多......这就是为什么我想要一个......更简单的解释。

  • Super() 必须遵循 MRO,即使父类上的继承不建议这样做?

  • Super() 无法转到父类的父类,因此如果父类中有 super 它将转到第二个继承的类?

  • 此外,在真实的工作场所环境中看到钻石问题有多少不相关但有多普遍?似乎是一种非常复杂的工作方式。

Jam*_*mes 5

您需要记住,MRO 不仅仅是简单的跟随领导者。它创建了一个类似图的结构并解析相同的继承以避免重复。在您的第一个示例中,您创建了一个菱形继承。

   A
  / \
 B   C
  \ /
   D
Run Code Online (Sandbox Code Playgroud)

MRO 寻求从第一级(直接父类)、从左到右(Bthen C)、然后再上一级、从左到右(就A在此处)等方法的解析。

在这种情况下,因为您拥有BC继承自A,顶级解析为单个A并创建上面的菱形图。

让我们看看你的第二个例子:

class D(B, C):
    def __init__(self):
        print("This is class D")
        B.__init__(self)
        C.__init__(self)
Run Code Online (Sandbox Code Playgroud)

通过以这种方式实施它,您可以有效地绕过 MRO。您已将继承钻石制成一个继承橄榄叉,如下所示:

 A   A
 |   |
 B   C
  \ /
   D
Run Code Online (Sandbox Code Playgroud)

因此,您调用了A两次初始化,这不需要发生。在长继承链或复杂的初始化例程中,这是非常低效的。