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

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)做出安全的选择。


当前回答

这两种方法都很有效。使用super()的方法为子类带来了更大的灵活性。

在直接调用方法中,C.__init__可以同时调用A.__init__和B.__init__。

当使用super()时,类需要设计为合作多重继承,其中C调用super,它调用A的代码,后者也将调用super,后者调用B的代码。有关使用super可以做什么,请参阅http://rhettinger.wordpress.com/2011/05/26/super-considered-super。

[稍后编辑的回答问题]

所以除非我知道/控制类的初始化,否则我 我不能对我所在的班级做出安全的选择 写作(C)。

参考的文章展示了如何通过在a和b周围添加包装器类来处理这种情况。在“如何合并非合作类”一节中有一个设计好的示例。

有人可能希望多重继承更简单,让你毫不费力地组合Car和Airplane类来获得FlyingCar,但现实是,单独设计的组件通常需要适配器或包装器,然后才能像我们希望的那样无缝地组合在一起:

另一个想法是:如果你对使用多重继承的组合功能不满意,你可以使用组合来完全控制在什么情况下调用哪些方法。

其他回答

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

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

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

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

Python 2.3方法解析顺序

正如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")

我添加了一个小型实用程序库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

如果你有多个来自第三方库的子类,那么不,没有盲目的方法来调用基类__init__方法(或任何其他方法),不管基类是如何编程的,它们实际上都是有效的。

Super使编写用于协作实现方法的类成为复杂的多重继承树的一部分成为可能,类作者不需要知道这些继承树。但是没有办法使用它来正确地从可能使用或不使用super的任意类继承。

Essentially, whether a class is designed to be sub-classed using super or with direct calls to the base class is a property which is part of the class' "public interface", and it should be documented as such. If you're using third-party libraries in the way that the library author expected and the library has reasonable documentation, it would normally tell you what you are required to do to subclass particular things. If not, then you'll have to look at the source code for the classes you're sub-classing and see what their base-class-invocation convention is. If you're combining multiple classes from one or more third-party libraries in a way that the library authors didn't expect, then it may not be possible to consistently invoke super-class methods at all; if class A is part of a hierarchy using super and class B is part of a hierarchy that doesn't use super, then neither option is guaranteed to work. You'll have to figure out a strategy that happens to work for each particular case.

这两种方法都很有效。使用super()的方法为子类带来了更大的灵活性。

在直接调用方法中,C.__init__可以同时调用A.__init__和B.__init__。

当使用super()时,类需要设计为合作多重继承,其中C调用super,它调用A的代码,后者也将调用super,后者调用B的代码。有关使用super可以做什么,请参阅http://rhettinger.wordpress.com/2011/05/26/super-considered-super。

[稍后编辑的回答问题]

所以除非我知道/控制类的初始化,否则我 我不能对我所在的班级做出安全的选择 写作(C)。

参考的文章展示了如何通过在a和b周围添加包装器类来处理这种情况。在“如何合并非合作类”一节中有一个设计好的示例。

有人可能希望多重继承更简单,让你毫不费力地组合Car和Airplane类来获得FlyingCar,但现实是,单独设计的组件通常需要适配器或包装器,然后才能像我们希望的那样无缝地组合在一起:

另一个想法是:如果你对使用多重继承的组合功能不满意,你可以使用组合来完全控制在什么情况下调用哪些方法。