用多个继承来调用父类__init__,正确的方法是什么?
假设我有一个多个继承方案:
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
's__ INT __ INT __
:( old-code)parent> parent> parentclass .__ init __(self)
- (新型)
super(derivedClass,self)
。 >)不遵循同一惯例,那么代码将无法正常工作(有些可能会错过一些,或多次被调用)。
那么正确的方法是什么?很容易说“只要保持一致,遵循一个或另一个”,但是如果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
)。
Say I have a multiple inheritance scenario:
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?
There's two typical approaches to writing C
's __init__
:
- (old-style)
ParentClass.__init__(self)
- (newer-style)
super(DerivedClass, self).__init__()
However, in either case, if the parent classes (A
and B
) don't follow the same convention, then the code will not work correctly (some may be missed, or get called multiple times).
So what's the correct way again? It's easy to say "just be consistent, follow one or the other", but if A
or B
are from a 3rd party library, what then? Is there an approach that can ensure that all parent class constructors get called (and in the correct order, and only once)?
Edit: to see what I mean, if I do:
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")
Then I get:
Entering C
Entering A
Entering B
Leaving B
Leaving A
Entering B
Leaving B
Leaving C
Note that B
's init gets called twice. If I do:
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")
Then I get:
Entering C
Entering A
Leaving A
Leaving C
Note that B
's init never gets called. So it seems that unless I know/control the init's of the classes I inherit from (A
and B
) I cannot make a safe choice for the class I'm writing (C
).
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(11)
您问题的答案取决于一个非常重要的方面:您的基类是为多个继承而设计的吗?
有3种不同的方案:
基类是无关的,独立的类。
如果您的基类是能够独立运作并且他们彼此不认识的独立实体,则它们不是为多个继承而设计的。示例:
重要:请注意,
foo
not bar call callsuper().__ INT __()
!这就是为什么您的代码无法正常工作的原因。由于钻石继承在python中的工作方式,基类是对象
不应调用super().__ INT __()
。正如您注意到的那样,这样做会破坏多个继承,因为您最终调用了另一类的__ INT __
而不是Object .__ INT __()
。 (免责声明:避免super()。在Python社区中达成共识。 rel =“ noreferrer”>适配器如果类不像您预期的那样行事。)
这也意味着您切勿编写从
对象
继承的类,并且没有__ INIT __
方法。不能定义__ Init __
方法与调用super().__ INT __()
的效果相同。如果您的类直接从对象
继承,请确保添加一个空的构造函数:无论如何,在这种情况下,您必须手动致电每个父构建器。有两种方法:
没有
超级
使用
超级
这两种方法中的每种都有其自身的优势和缺点。如果您使用
super
,您的类将支持依赖项注入。另一方面,犯错很容易。例如,如果更改foo
和bar
(例如类Foobar(bar,foo)
)的顺序,则必须更新超级
匹配调用。没有超级
您不必为此担心,并且代码更具可读性。其中一个是混合物。
a mixin 是一类是< em>设计可与多个继承一起使用。这意味着我们不必手动调用两个父构造函数,因为Mixin会自动为我们调用第二个构造函数。由于这次我们只需要调用单个构造函数,因此我们可以使用
super
来避免必须硬编码父类的名称。示例:
这里的重要细节是:
super()。
子类从Mixin first :
类Foobar(Foomixin,bar)
class。如果基类的顺序是错误的,则永远不会称呼Mixin的构造函数。所有基础类都是为合作继承而设计的。
为合作继承而设计的课程与Mixins很像:它们通过所有未使用的论点将其传递给下一阶级。像以前一样,我们只需要调用
super().__ INT __()
,所有父构建符都将被链接。示例:
在这种情况下,父类的顺序无关紧要。我们也可以先从
Coopbar
首先继承,并且代码仍然可以工作。但这仅此而已,因为所有参数均以关键字参数传递。使用位置参数将使参数的顺序变得容易,因此合作类仅接受关键字参数是习惯的。这也是我前面提到的规则的例外:
copfoo
和cookbar
从object
> super().__ init __()。如果没有,就不会有合作的继承。底线:正确的实现取决于您继承的类。
构造函数是班级公共界面的一部分。如果该类被设计为混合蛋白或合作继承,则必须记录在案。如果文档没有提及任何类型的内容,则可以肯定地假设该类不是为合作多重继承而设计的。
The answer to your question depends on one very important aspect: Are your base classes designed for multiple inheritance?
There are 3 different scenarios:
The base classes are unrelated, standalone classes.
If your base classes are separate entities that are capable of functioning independently and they don't know each other, they're not designed for multiple inheritance. Example:
Important: Notice that neither
Foo
norBar
callssuper().__init__()
! This is why your code didn't work correctly. Because of the way diamond inheritance works in python, classes whose base class isobject
should not callsuper().__init__()
. As you've noticed, doing so would break multiple inheritance because you end up calling another class's__init__
rather thanobject.__init__()
. (Disclaimer: Avoidingsuper().__init__()
inobject
-subclasses is my personal recommendation and by no means an agreed-upon consensus in the python community. Some people prefer to usesuper
in every class, arguing that you can always write an adapter if the class doesn't behave as you expect.)This also means that you should never write a class that inherits from
object
and doesn't have an__init__
method. Not defining a__init__
method at all has the same effect as callingsuper().__init__()
. If your class inherits directly fromobject
, make sure to add an empty constructor like so:Anyway, in this situation, you will have to call each parent constructor manually. There are two ways to do this:
Without
super
With
super
Each of these two methods has its own advantages and disadvantages. If you use
super
, your class will support dependency injection. On the other hand, it's easier to make mistakes. For example if you change the order ofFoo
andBar
(likeclass FooBar(Bar, Foo)
), you'd have to update thesuper
calls to match. Withoutsuper
you don't have to worry about this, and the code is much more readable.One of the classes is a mixin.
A mixin is a class that's designed to be used with multiple inheritance. This means we don't have to call both parent constructors manually, because the mixin will automatically call the 2nd constructor for us. Since we only have to call a single constructor this time, we can do so with
super
to avoid having to hard-code the parent class's name.Example:
The important details here are:
super().__init__()
and passes through any arguments it receives.class FooBar(FooMixin, Bar)
. If the order of the base classes is wrong, the mixin's constructor will never be called.All base classes are designed for cooperative inheritance.
Classes designed for cooperative inheritance are a lot like mixins: They pass through all unused arguments to the next class. Like before, we just have to call
super().__init__()
and all parent constructors will be chain-called.Example:
In this case, the order of the parent classes doesn't matter. We might as well inherit from
CoopBar
first, and the code would still work the same. But that's only true because all arguments are passed as keyword arguments. Using positional arguments would make it easy to get the order of the arguments wrong, so it's customary for cooperative classes to accept only keyword arguments.This is also an exception to the rule I mentioned earlier: Both
CoopFoo
andCoopBar
inherit fromobject
, but they still callsuper().__init__()
. If they didn't, there would be no cooperative inheritance.Bottom line: The correct implementation depends on the classes you're inheriting from.
The constructor is part of a class's public interface. If the class is designed as a mixin or for cooperative inheritance, that must be documented. If the docs don't mention anything of the sort, it's safe to assume that the class isn't designed for cooperative multiple inheritance.
两种方式都可以正常工作。使用
super()
的方法可为子类带来更大的灵活性。在直接呼叫方法中,
c .__ Init __
可以调用a .__ INT __
和b .__ INT __ INT __
。使用
super()
时,需要为合作的多重继承而设计,其中c
呼叫super
,它调用a 的代码也将调用
超级
调用b
的代码。参见 http://rhettinger.wordpress.com/2011/05/26/超级考虑的super 有关super
可以完成的操作的更多详细信息。[以后编辑的回答问题]
引用的文章通过在
a
和b
上添加包装类别来显示如何处理这种情况。标题为“如何合并非合作类”的部分中有一个锻炼示例。人们可能希望多种继承更容易,让您毫不费力地组合汽车和飞机班来获得飞行车,但是现实是,单独设计的组件通常需要适配器或包装器,然后才能像我们想要的那样无缝地安装在一起
。 :如果您对使用多个继承的功能不满意,则可以使用构图来完全控制哪些情况的方法。
Both ways work fine. The approach using
super()
leads to greater flexibility for subclasses.In the direct call approach,
C.__init__
can call bothA.__init__
andB.__init__
.When using
super()
, the classes need to be designed for cooperative multiple inheritance whereC
callssuper
, which invokesA
's code which will also callsuper
which invokesB
's code. See http://rhettinger.wordpress.com/2011/05/26/super-considered-super for more detail on what can be done withsuper
.[Response question as later edited]
The referenced article shows how to handle this situation by adding a wrapper class around
A
andB
. There is a worked-out example in the section titled "How to Incorporate a Non-cooperative Class".One might wish that multiple inheritance were easier, letting you effortlessly compose Car and Airplane classes to get a FlyingCar, but the reality is that separately designed components often need adapters or wrappers before fitting together as seamlessly as we would like :-)
One other thought: if you're unhappy with composing functionality using multiple inheritance, you can use composition for complete control over which methods get called on which occasions.
如果您控制了
a
和b
,则方法(“新样式”或“旧样式”)将起作用。否则,可能需要使用适配器类。源代码可访问:此处正确使用“新样式”
,方法解决顺序(mro)以下:
c(a,b)
首先指示a
,然后b
。 mro是c - &gt; a - &gt; b - &gt;对象
。super(a,self)。
super(b,self)。
您可以说这种情况是为多个继承而设计的。
源代码可访问:在此处正确使用“旧样式”
,MRO并不重要,因为
a .__ INT __
和b .__ INT __ INT __
被明确称为。c(b,a)类:
也可以正常工作。尽管这种情况并不是上一款以新样式的多种继承为“设计”的,但仍然可以使用多个继承。
现在,如果
a
和b
来自第三方库 - 即,您无法控制a
的源代码和b
?简短的答案:您必须设计一个实现必要super
调用的适配器类,然后使用空班来定义MRO(请参阅 raymond hettinger在super
上的文章 - 尤其是“如何合并非合件类”部分” )。第三方父母:
a
不实现super
;b
class
ADAPTER
实施super
,以便c
可以定义MRO,当>
时会发挥作用超级(适配器,self).__ init __()
被执行。如果相反,该怎么办?
第三方父母:
a
实现super
;b
在此处不同,除了执行顺序在
适配器.__ INIT __
中切换。超级
首先致电,然后呼叫。请注意,每个与第三方父母的案例都需要独特的适配器课。尽管您可以处理 control
a
和b
的源代码的情况,但是您必须知道父母类的初始性如何实现super
(如果有的话)才能这样做。Either approach ("new style" or "old style") will work if you have control over the source code for
A
andB
. Otherwise, use of an adapter class might be necessary.Source code accessible: Correct use of "new style"
Here, method resolution order (MRO) dictates the following:
C(A, B)
dictatesA
first, thenB
. MRO isC -> A -> B -> object
.super(A, self).__init__()
continues along the MRO chain initiated inC.__init__
toB.__init__
.super(B, self).__init__()
continues along the MRO chain initiated inC.__init__
toobject.__init__
.You could say that this case is designed for multiple inheritance.
Source code accessible: Correct use of "old style"
Here, MRO does not matter, since
A.__init__
andB.__init__
are called explicitly.class C(B, A):
would work just as well.Although this case is not "designed" for multiple inheritance in the new style as the previous one was, multiple inheritance is still possible.
Now, what if
A
andB
are from a third party library - i.e., you have no control over the source code forA
andB
? The short answer: You must design an adapter class that implements the necessarysuper
calls, then use an empty class to define the MRO (see Raymond Hettinger's article onsuper
- especially the section, "How to Incorporate a Non-cooperative Class").Third-party parents:
A
does not implementsuper
;B
doesClass
Adapter
implementssuper
so thatC
can define the MRO, which comes into play whensuper(Adapter, self).__init__()
is executed.And what if it's the other way around?
Third-party parents:
A
implementssuper
;B
does notSame pattern here, except the order of execution is switched in
Adapter.__init__
;super
call first, then explicit call. Notice that each case with third-party parents requires a unique adapter class.Although you can handle the cases where you don't control the source code of
A
andB
by using an adapter class, it is true that you must know how the init's of the parent classes implementsuper
(if at all) in order to do so.正如雷蒙德(Raymond)在他的回答中所说的那样,直接呼叫
a .__ Init __
和b .__ INT __
正常工作,您的代码将是可读的。但是,它不使用
c
和这些类之间的继承链接。利用该链接可以使您更加稳定,并使最终的重构更容易且容易出错。如何做到这一点的一个例子:As Raymond said in his answer, a direct call to
A.__init__
andB.__init__
works fine, and your code would be readable.However, it does not use the inheritance link between
C
and those classes. Exploiting that link gives you more consistancy and make eventual refactorings easier and less error-prone. An example of how to do that:如果您是从第三方库乘以子类别类,则不,没有盲目的方法来调用基类
__ INT __ INT __
方法(或任何其他方法),无论基本类别如何已编程。super
使可能编写旨在合作实现方法的类,作为复杂的多个继承树的一部分,这是班级作者不需要的。但是,没有办法使用它来正确继承可能使用super
的任意类。本质上,无论是使用
super
还是直接调用基类的班级是属于类的属性,是类“公共接口”的一部分,应将其记录为这样的。如果您以图书馆作者的预期和库具有合理的文档的方式使用第三方库,则通常会告诉您您需要做些什么来为特定的事情。如果不是,那么您必须查看您的子类别的类的源代码,并查看其基础阶级定位约定是什么。如果您以一个或多个第三方库中的多个类的方式组合了库作者没有期望的方式,那么可能不可能始终如一地调用超级级别的方法 根本;如果A类使用super
,而B类是不使用Super的层次结构的一部分,那么都不保证任何选项可以正常工作。您必须弄清楚适合每种特定情况的策略。If you are multiply sub-classing classes from third party libraries, then no, there is no blind approach to calling the base class
__init__
methods (or any other methods) that actually works regardless of how the base classes are programmed.super
makes it possible to write classes designed to cooperatively implement methods as part of complex multiple inheritance trees which need not be known to the class author. But there's no way to use it to correctly inherit from arbitrary classes that may or may not usesuper
.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 usingsuper
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.本文有助于解释合作的多个继承:
或在Python 3中使用Super 3
它提到了有用的方法
mro()
向您显示方法分辨率订单。在您的第二个示例中,您在a
中调用super
,super
呼叫继续在 mro 。顺序中的下一个类是b
,这就是为什么b
的init首次称为INIT的原因。这是官方Python网站的一篇更具技术性的文章:
python 2.3方法分辨率分辨率顺序
This article helps to explain cooperative multiple inheritance:
The wonders of cooperative inheritance, or using super in Python 3
It mentions the useful method
mro()
that shows you the method resolution order. In your second example, where you callsuper
inA
, thesuper
call continues on in MRO. The next class in the order isB
, this is whyB
's init is called the first time.Here's a more technical article from the official Python site:
The Python 2.3 Method Resolution Order
我添加了一个小型实用库, supers ,这使得这种情况变得更容易处理。它的工作如下:
创建C时输出:输出
I added a small utility library, supers, which makes this kind of scenario simpler to handle. It works as follows:
Output when creating C:
这是我使用super()在Python&nbsp; 3中实现多个继承的方法
Here is how I have implemented the multiple inheritance in Python 3 using super()
这是我在Python继承中实现超级方法的方式并实现了所需的解决方案:
Here is how I have implemented the super method in Python inheritance and achieved the required solution:
首先,假设您得到 mro 链
Firstly, suppose you got the MRO chain
From the lowest level subclass init method on, any class which using super() method would jump into corresponding chain position, as any class which not using super() method would jump out corresponding chain position.
它遵循 mro rule rule和 ainit 。
It follows the MRO rule and A init is called.