调用父类__init__多重inheritance,什么是正确的方法?
假设我有一个多inheritance的场景:
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__(self)
- (newer-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
)inheritance的类的init,否则我不能为我正在写的类( C
)做出安全的select。
这两种方式工作正常。 使用super()
的方法为子类提供了更大的灵活性。
在直接调用方法中, C.__init__
可以调用A.__init__
和B.__init__
。
当使用super()
,类需要被devise为协调多重inheritance,其中C
调用super
,调用A
的代码,也将调用super
调用B
的代码。 请参阅http://rhettinger.wordpress.com/2011/05/26/super-considered-super了解更多有关可以使用;super
进行的更多细节。
[回答问题,稍后编辑]
所以,除非我知道/控制我从(A和B)inheritance的类的init,否则我不能为我正在写的类(C)做出安全的select。
引用的文章显示了如何通过在A
和B
周围添加包装类来处理这种情况。 在“如何纳入非合作class级”一节中有一个解决的例子。
人们可能希望多inheritance更容易,让你毫不费力地组成汽车和飞机类来获得一个FlyingCar,但事实是,独立devise的组件往往需要适配器或包装之前,无缝地assembly在一起,我们希望:-)
另外一个想法是:如果您对使用多重inheritance的组合function感到不满意,您可以使用组合来完全控制在哪些场合调用哪些方法。
本文有助于解释协作式多重inheritance:
http://www.artima.com/weblogs/viewpost.jsp?thread=281127
它提到了有用的方法mro()
,它显示了你的方法parsing顺序。 在你的第二个例子中,你在A
呼叫super
, super
呼叫在MRO中继续。 下一个class的顺序是B
,这就是为什么B
的init是第一次被调用的。
以下是来自官方python站点的更多技术文章:
如果你是从第三方库中分类出来的子类,那么不需要,不pipe基类是如何编程的,调用基类__init__
方法(或其他任何方法)都没有盲目的方法。
super
使得可以编写被devise为协作地实现方法的类作为复杂的多重inheritance树的一部分,这对于类作者是不需要知道的。 但是没有办法使用它来正确地inheritance可能或不可能使用super
任意类。
从本质上讲,一个类是否被devise为使用super
类进行子分类,或者直接调用基类是属于该类的“公共接口”的一个属性,它应该被logging下来。 如果您以图书馆作者所期望的方式使用第三方库,并且图书馆具有合理的文档,那么通常会告诉您您需要做些什么来inheritance特定的东西。 如果没有,那么你将不得不看看你的子类的源代码,看看他们的基类调用约定是什么。 如果你以一个图书馆作者没有想到的方式把一个或多个第三方库中的多个类组合起来,那么根本就不可能一致地调用超类方法; 如果类A是使用super
的层次结构的一部分,并且类B是不使用super的层次结构的一部分,那么这两个选项都不能保证工作。 你将不得不找出适合每个特定情况的策略。