在Python中为多个父类调用Super()__init__

huangapple go评论135阅读模式
英文:

Super()__init__ on multiple parent classes for Python

问题

这是因为在多重继承中,Python 使用 C3 线性化算法来确定方法解析顺序(Method Resolution Order,MRO)。尽管在类定义中 Derived 继承顺序是 Base1, Base2,但 MRO 实际上是根据 C3 算法确定的,以确保在多重继承情况下方法解析的一致性。

因此,当你创建 Derived 的实例时,它会按照 MRO 的顺序来调用构造函数。在这种情况下,MRO 是 Base1, Base2,所以首先调用的是 Base2 的 __init__ 方法,然后才是 Base1 的 __init__ 方法,这就是为什么你看到输出中先打印了 "Base2 init"。

这个行为是 Python 多重继承的一部分,它确保了方法解析的一致性和可预测性。

英文:
class Base1:
    def __init__(self):
        super().__init__()
        print("Base1 __init__")

class Base2:
    def __init__(self):
        print("Base2 __init__")

class Derived(Base1, Base2):
     pass

# Create an instance of the Derived class
d = Derived()

This outputs:

   
Base2 __init__
Base1 __init__

What principle or mechanism makes it print Base2 __init__ ?

Isn't MRO on Derived supposed to be Base1->Base2, and thus only Base1's init should be run?

答案1

得分: 4

这与一直以来的原理相同,super 获取方法解析顺序中的下一个方法

现在,这是Derived的方法解析顺序(mro):

>>> Derived.mro()
[<class '__main__.Derived'>, <class '__main__.Base1'>, <class '__main__.Base2'>, <class 'object'>]

因此,在Derived上解析__init__时,首先检查是否存在Derived.__init__。它不存在,然后检查Base1.__init__,并找到它,然后调用它。

Base1.__init__内,您调用super().__init__()。现在,理解这一点至关重要,隐式参数是super(Base1, self)。因此,由于此处的selfDerived的一个实例,将会查看Derived的方法解析顺序。然后super 决定获取传递给它的类之后方法解析顺序中的下一个类。所以:

[<class '__main__.Derived'>, <class '__main__.Base1'>, <class '__main__.Base2'>, <class 'object'>]
                              ^^^                      ^^^
                              我们在这里              所以我们选择这个类

Base1之后方法解析顺序中的下一个类是Base2,因此它调用Base2.__init__

这就是它应该工作的方式。这就是super的美妙之处!

英文:

It's the same principle as always, super gets the next method in the method resolution order.

Now, here is the mro of Derived:

&gt;&gt;&gt; Derived.mro()
[&lt;class &#39;__main__.Derived&#39;&gt;, &lt;class &#39;__main__.Base1&#39;&gt;, &lt;class &#39;__main__.Base2&#39;&gt;, &lt;class &#39;object&#39;&gt;]

So when __init__ is resolved on Derived, it first checks if Derived.__init__ exists. It doesn't, so then it checks Base1.__init__, and finds it, and it is called.

Inside Base1.__init__, you call super().__init__(). Now this is crucial to understand, the implicit arguments are super(Base1, self). So, since self here is an instance of Derived, the mro of Derived is consulted. And then super decides to take the next class in the MRO after the class that was passed to super. So:

[&lt;class &#39;__main__.Derived&#39;&gt;, &lt;class &#39;__main__.Base1&#39;&gt;, &lt;class &#39;__main__.Base2&#39;&gt;, &lt;class &#39;object&#39;&gt;]
                              ^^^                      ^^^
                              we are here              so we pick this one 

And the next class in the MRO after Base1 is Base2, so it calls Base2.__init__

This is all how it's supposed to work. This is the beauty behind super!

huangapple
  • 本文由 发表于 2023年6月6日 13:09:06
  • 转载请务必保留本文链接:https://go.coder-hub.com/76411581.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定