python - 为什么super()继承 "wrong"类?

标签 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()

This is class D
This is class B
This is class C
This is class A

它按预期工作,这很好,但我想知道为什么 super().__init__()class 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'>)

但我不知道为什么。

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


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

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

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

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


i = D()
(<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.A'>, <class 'object'>)

情况恰恰相反,我知道 MRO 是正确的,但奇怪的是实际执行却并非如此:

This is class D
This is class B
This is class A
This is class C
This is class A

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

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

  • 即使父类的继承另有建议,Super() 也必须遵循 MRO?

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

  • 此外,有点不相关,但在真实的工作环境中看到菱形继承(钻石问题)有多常见?看起来是一种非常复杂的工作方式。

最佳答案

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

   A
  / \
 B   C
  \ /
   D

MRO 从第一级(直接父类)开始寻求方法解析,从左到右(B 然后 C),然后是下一个级别,从左到右(这里只是 A),依此类推。

在本例中,由于 BC 均继承自 A,因此顶层解析为单个 A 并创建上面的菱形图。

让我们看一下第二个示例:

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

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

 A   A
 |   |
 B   C
  \ /
   D

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

关于python - 为什么super()继承 "wrong"类?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/59975088/

相关文章:

python - Django 1.8 基于变量的动态url配置

python - 获取list python的所有有序组合

python - 如何在特定值下对 kdeplot 进行着色

iphone - 'super' 在我的应用程序中到底在做什么?

c++ - 创建类的过程涉及虚继承

python - 如何用变形2制作水平形状?

java - 在Java中_not_调用父类(super class)构造函数的任何方法?

android - Apksigner 没有在 termux 上签署 apk

具有相同名称的 C++ 虚拟覆盖函数

c++ - 虚拟派生的多态类的大小