假设我有一个多重继承的场景:

class A(object):
    # code for A here

class B(object):
    # code for B here

class C(A, B):
    def __init__(self):
        # What's the right code to write here to ensure 
        # A.__init__ and B.__init__ get called?

有两种典型的方法来编写C语言的__init__:

(老式)ParentClass.__init__(自我) (new -style) super(DerivedClass, self).__init__()

然而,在任何一种情况下,如果父类(A和B)不遵循相同的约定,那么代码将不能正确工作(有些可能会丢失,或被多次调用)。

正确的方法是什么来着?说“保持一致,遵循其中一个”很容易,但如果A或B来自第三方库,那怎么办?是否有一种方法可以确保所有父类构造函数都被调用(并且以正确的顺序,并且只调用一次)?

编辑:看看我的意思,如果我这样做:

class A(object):
    def __init__(self):
        print("Entering A")
        super(A, self).__init__()
        print("Leaving A")

class B(object):
    def __init__(self):
        print("Entering B")
        super(B, self).__init__()
        print("Leaving B")

class C(A, B):
    def __init__(self):
        print("Entering C")
        A.__init__(self)
        B.__init__(self)
        print("Leaving C")

然后我得到:

Entering C
Entering A
Entering B
Leaving B
Leaving A
Entering B
Leaving B
Leaving C

注意B的init被调用了两次。如果我这样做:

class A(object):
    def __init__(self):
        print("Entering A")
        print("Leaving A")

class B(object):
    def __init__(self):
        print("Entering B")
        super(B, self).__init__()
        print("Leaving B")

class C(A, B):
    def __init__(self):
        print("Entering C")
        super(C, self).__init__()
        print("Leaving C")

然后我得到:

Entering C
Entering A
Leaving A
Leaving C

注意B的init从未被调用。因此,似乎除非我知道/控制从(A和B)继承的类的初始化,否则我无法为我正在编写的类(C)做出安全的选择。


当前回答

我添加了一个小型实用程序库superers,它使这种场景更容易处理。其工作原理如下:

class A(object):
    def __init__(self):
        print("Entering A")
        print("Leaving A")

class B(object):
    def __init__(self):
        print("Entering B")
        super(B, self).__init__()
        print("Leaving B")

class C(A, B):
    def __init__(self):
        print("Entering C")
        supers(self).__init__()
        print("Leaving C")

创建C时输出:

Entering C
Entering A
Leaving A
Entering B
Leaving B
Leaving C

其他回答

它遵循MRO规则,并调用init。

首先,假设您有MRO链 从最底层的子类init方法开始,任何使用super()方法的类都将跳转到相应的链位置,而任何不使用super()方法的类也将跳转到相应的链位置。

正如Raymond在他的回答中所说,直接调用a .__init__和B.__init__工作正常,您的代码将是可读的。

但是,它不使用C和这些类之间的继承链接。利用该链接可以提供更多的一致性,并使最终的重构更容易,更不容易出错。如何做到这一点的例子:

class C(A, B):
    def __init__(self):
        print("entering c")
        for base_class in C.__bases__:  # (A, B)
             base_class.__init__(self)
        print("leaving c")

下面是我如何在Python 3中使用super()实现多重继承

class A:
  def __init__(self, a, b, **kwargs):
      print("Class A initiallised")
      self.a = a
      self.b = b
      super().__init__(**kwargs)
      print("Class A initiallisation done")

  def __str__(self):
      return f"{self.a} and {self.b}"

  def display_a(self):
      return f"{self.a} and {self.b}"

class C:
   def __init__(self, c, d, **kwargs):
      print("Class C initiallised")
      self.c = c
      self.d = d
      super().__init__(**kwargs)
      print("class c initiallisation done")

   def __str__(self):
      return f"{self.c} and {self.d}"

   def display_c(self):
       return f"{self.c} and {self.d}"


class D(A,C):
   def __init__(self, e, **kwargs):
       print("Class D initiallised")
       super().__init__(**kwargs)
       self.e = e
       print("Class D initiallisation done")

   def __str__(self):
      return f"{self.e} is e,{self.b} is b,{self.a} is a,{self.d} is d,{self.c} is c"

if __name__ == "__main__":
   d = D(a=12, b=13, c=14, d=15, e=16)
   print(d)
   d.display_c()
   d.display_a()

本文有助于解释合作多重继承:

合作继承的奇迹,或者在python3中使用super

它提到了有用的方法mro(),该方法向您显示了方法解析顺序。在第二个示例中,在A中调用super,在MRO中继续执行super调用。顺序下一个类是B,这就是为什么B的init第一次被调用。

以下是来自Python官方网站的一篇更具技术性的文章:

Python 2.3方法解析顺序