Python 的 super() 如何处理多重继承?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/3277367/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How does Python's super() work with multiple inheritance?
提问by Callisto
I'm pretty much new in Python object oriented programming and I have trouble
understanding the super()function (new style classes) especially when it comes to multiple inheritance.
我对 Python 面向对象编程还很陌生,我无法理解super()函数(新样式类),尤其是在涉及多重继承时。
For example if you have something like:
例如,如果你有类似的东西:
class First(object):
def __init__(self):
print "first"
class Second(object):
def __init__(self):
print "second"
class Third(First, Second):
def __init__(self):
super(Third, self).__init__()
print "that's it"
What I don't get is: will the Third()class inherit both constructor methods? If yes, then which one will be run with super() and why?
我不明白的是:Third()该类会继承两个构造函数方法吗?如果是,那么哪一个将使用 super() 运行,为什么?
And what if you want to run the other one? I know it has something to do with Python method resolution order (MRO).
如果你想运行另一个怎么办?我知道这与 Python 方法解析顺序(MRO)有关。
采纳答案by rbp
This is detailed with a reasonable amount of detail by Guido himself in his blog post Method Resolution Order(including two earlier attempts).
Guido 本人在他的博客文章Method Resolution Order(包括两次较早的尝试)中详细介绍了这一点。
In your example, Third()will call First.__init__. Python looks for each attribute in the class's parents as they are listed left to right. In this case, we are looking for __init__. So, if you define
在您的示例中,Third()将调用First.__init__. Python 在类的父类中查找从左到右列出的每个属性。在这种情况下,我们正在寻找__init__. 所以,如果你定义
class Third(First, Second):
...
Python will start by looking at First, and, if Firstdoesn't have the attribute, then it will look at Second.
Python 将首先查看First,如果First没有该属性,则它会查看Second。
This situation becomes more complex when inheritance starts crossing paths (for example if Firstinherited from Second). Read the link above for more details, but, in a nutshell, Python will try to maintain the order in which each class appears on the inheritance list, starting with the child class itself.
当继承开始跨越路径时,这种情况变得更加复杂(例如,如果First继承自Second)。阅读上面的链接以获取更多详细信息,但简而言之,Python 将尝试维护每个类出现在继承列表中的顺序,从子类本身开始。
So, for instance, if you had:
因此,例如,如果您有:
class First(object):
def __init__(self):
print "first"
class Second(First):
def __init__(self):
print "second"
class Third(First):
def __init__(self):
print "third"
class Fourth(Second, Third):
def __init__(self):
super(Fourth, self).__init__()
print "that's it"
the MRO would be [Fourth, Second, Third, First].
MRO 将是 [Fourth, Second, Third, First].
By the way: if Python cannot find a coherent method resolution order, it'll raise an exception, instead of falling back to behavior which might surprise the user.
顺便说一句:如果 Python 找不到一致的方法解析顺序,它将引发异常,而不是退回到可能让用户感到惊讶的行为。
Edited to add an example of an ambiguous MRO:
编辑添加一个模棱两可的 MRO 的例子:
class First(object):
def __init__(self):
print "first"
class Second(First):
def __init__(self):
print "second"
class Third(First, Second):
def __init__(self):
print "third"
Should Third's MRO be [First, Second]or [Second, First]? There's no obvious expectation, and Python will raise an error:
应该Third是 MRO[First, Second]还是[Second, First]?没有明显的期望,Python 会引发错误:
TypeError: Error when calling the metaclass bases
Cannot create a consistent method resolution order (MRO) for bases Second, First
Edit:I see several people arguing that the examples above lack super()calls, so let me explain: The point of the examples is to show how the MRO is constructed. They are notintended to print "first\nsecond\third" or whatever. You can – and should, of course, play around with the example, add super()calls, see what happens, and gain a deeper understanding of Python's inheritance model. But my goal here is to keep it simple and show how the MRO is built. And it is built as I explained:
编辑:我看到有几个人认为上面的例子缺少super()调用,所以让我解释一下:这些例子的重点是展示 MRO 是如何构建的。它们不打算打印“第一\n第二\第三”或其他任何内容。您可以——当然也应该使用示例,添加super()调用,看看会发生什么,并更深入地了解 Python 的继承模型。但我的目标是保持简单并展示 MRO 是如何构建的。它是按照我的解释构建的:
>>> Fourth.__mro__
(<class '__main__.Fourth'>,
<class '__main__.Second'>, <class '__main__.Third'>,
<class '__main__.First'>,
<type 'object'>)
回答by monoceres
This is known as the Diamond Problem, the page has an entry on Python, but in short, Python will call the superclass's methods from left to right.
这被称为钻石问题,该页面有一个关于 Python 的条目,但简而言之,Python 会从左到右调用超类的方法。
回答by lifeless
Your code, and the other answers, are all buggy. They are missing the super()calls in the first two classes that are required for co-operative subclassing to work.
您的代码和其他答案都有问题。他们缺少super()前两个类中的调用,这些调用是协作子类化工作所需的。
Here is a fixed version of the code:
这是代码的固定版本:
class First(object):
def __init__(self):
super(First, self).__init__()
print("first")
class Second(object):
def __init__(self):
super(Second, self).__init__()
print("second")
class Third(First, Second):
def __init__(self):
super(Third, self).__init__()
print("third")
The super()call finds the next method in the MRO at each step, which is why First and Second have to have it too, otherwise execution stops at the end of Second.__init__().
该super()调用在每一步查找 MRO 中的下一个方法,这就是为什么 First 和 Second 也必须拥有它,否则执行会在Second.__init__().
This is what I get:
这就是我得到的:
>>> Third()
second
first
third
回答by Seaux
I understand this doesn't directly answer the super()question, but I feel it's relevant enough to share.
我知道这并不能直接回答super()问题,但我觉得它足够重要,可以分享。
There is also a way to directly call each inherited class:
还有一种方法可以直接调用每个继承的类:
class First(object):
def __init__(self):
print '1'
class Second(object):
def __init__(self):
print '2'
class Third(First, Second):
def __init__(self):
Second.__init__(self)
Just note that if you do it this way, you'll have to call each manually as I'm pretty sure First's __init__()won't be called.
请注意,如果您这样做,则必须手动调用每个,因为我很确定First's__init__()不会被调用。
回答by Trilarion
Another not yet covered point is passing parameters for initialization of classes. Since the destination of superdepends on the subclass the only good way to pass parameters is packing them all together. Then be careful to not have the same parameter name with different meanings.
另一个尚未涉及的点是为类的初始化传递参数。由于 的目标super取决于子类,因此传递参数的唯一好方法是将它们打包在一起。然后注意不要有不同含义的相同参数名称。
Example:
例子:
class A(object):
def __init__(self, **kwargs):
print('A.__init__')
super().__init__()
class B(A):
def __init__(self, **kwargs):
print('B.__init__ {}'.format(kwargs['x']))
super().__init__(**kwargs)
class C(A):
def __init__(self, **kwargs):
print('C.__init__ with {}, {}'.format(kwargs['a'], kwargs['b']))
super().__init__(**kwargs)
class D(B, C): # MRO=D, B, C, A
def __init__(self):
print('D.__init__')
super().__init__(a=1, b=2, x=3)
print(D.mro())
D()
gives:
给出:
[<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.A'>, <class 'object'>]
D.__init__
B.__init__ 3
C.__init__ with 1, 2
A.__init__
Calling the super class __init__directly to more direct assignment of parameters is tempting but fails if there is any supercall in a super class and/or the MRO is changed and class A may be called multiple times, depending on the implementation.
__init__直接调用超类以进行更直接的参数分配很诱人,但如果super超类中有任何调用和/或 MRO 更改并且类 A 可能会被多次调用,则取决于实现,则失败。
To conclude: cooperative inheritance and super and specific parameters for initialization aren't working together very well.
总结:协作继承和 super 以及用于初始化的特定参数不能很好地协同工作。
回答by brent.payne
This is to how I solved to issue of having multiple inheritance with different variables for initialization and having multiple MixIns with the same function call. I had to explicitly add variables to passed **kwargs and add a MixIn interface to be an endpoint for super calls.
这就是我如何解决具有用于初始化的不同变量的多重继承以及具有相同函数调用的多个 MixIn 的问题。我必须显式地向传递的 **kwargs 添加变量,并添加一个 MixIn 接口作为超级调用的端点。
Here Ais an extendable base class and Band Care MixIn classes both who provide function f. Aand Bboth expect parameter vin their __init__and Cexpects w.
The function ftakes one parameter y. Qinherits from all three classes. MixInFis the mixin interface for Band C.
这A是一个可扩展的基类,B和C都是提供功能的 MixIn 类f。 A并且B都期望参数v在他们的__init__和C期望中w。该函数f采用一个参数y。 Q继承自所有三个类。MixInF对于混合接口B和C。
class A(object):
def __init__(self, v, *args, **kwargs):
print "A:init:v[{0}]".format(v)
kwargs['v']=v
super(A, self).__init__(*args, **kwargs)
self.v = v
class MixInF(object):
def __init__(self, *args, **kwargs):
print "IObject:init"
def f(self, y):
print "IObject:y[{0}]".format(y)
class B(MixInF):
def __init__(self, v, *args, **kwargs):
print "B:init:v[{0}]".format(v)
kwargs['v']=v
super(B, self).__init__(*args, **kwargs)
self.v = v
def f(self, y):
print "B:f:v[{0}]:y[{1}]".format(self.v, y)
super(B, self).f(y)
class C(MixInF):
def __init__(self, w, *args, **kwargs):
print "C:init:w[{0}]".format(w)
kwargs['w']=w
super(C, self).__init__(*args, **kwargs)
self.w = w
def f(self, y):
print "C:f:w[{0}]:y[{1}]".format(self.w, y)
super(C, self).f(y)
class Q(C,B,A):
def __init__(self, v, w):
super(Q, self).__init__(v=v, w=w)
def f(self, y):
print "Q:f:y[{0}]".format(y)
super(Q, self).f(y)
回答by Visionscaper
I wanted to elaborate the answer by lifelessa bit because when I started reading about how to use super() in a multiple inheritance hierarchy in Python, I did't get it immediately.
我想详细说明一下这个答案,因为当我开始阅读如何在 Python 的多重继承层次结构中使用 super() 时,我没有立即明白。
What you need to understand is that super(MyClass, self).__init__()provides the next__init__method according to the used Method Resolution Ordering (MRO) algorithm in the context of the complete inheritance hierarchy.
您需要了解的是,在完整继承层次结构的上下文中,根据使用的方法解析排序(MRO)算法super(MyClass, self).__init__()提供下一个__init__方法。
This last part is crucial to understand. Let's consider the example again:
最后一部分对于理解至关重要。让我们再次考虑这个例子:
#!/usr/bin/env python2
class First(object):
def __init__(self):
print "First(): entering"
super(First, self).__init__()
print "First(): exiting"
class Second(object):
def __init__(self):
print "Second(): entering"
super(Second, self).__init__()
print "Second(): exiting"
class Third(First, Second):
def __init__(self):
print "Third(): entering"
super(Third, self).__init__()
print "Third(): exiting"
According to this article about Method Resolution Orderby Guido van Rossum, the order to resolve __init__is calculated (before Python 2.3) using a "depth-first left-to-right traversal" :
根据Guido van Rossum关于方法解析顺序的这篇文章,解析顺序__init__是使用“深度优先从左到右遍历”计算的(在 Python 2.3 之前):
Third --> First --> object --> Second --> object
After removing all duplicates, except for the last one, we get :
删除除最后一个之外的所有重复项后,我们得到:
Third --> First --> Second --> object
So, lets follow what happens when we instantiate an instance of the Thirdclass, e.g. x = Third().
所以,让我们看看当我们实例化一个Third类的实例时会发生什么,例如x = Third()。
- According to MRO
Third.__init__executes.- prints
Third(): entering - then
super(Third, self).__init__()executes and MRO returnsFirst.__init__which is called.
- prints
First.__init__executes.- prints
First(): entering - then
super(First, self).__init__()executes and MRO returnsSecond.__init__which is called.
- prints
Second.__init__executes.- prints
Second(): entering - then
super(Second, self).__init__()executes and MRO returnsobject.__init__which is called.
- prints
object.__init__executes (no print statements in the code there)- execution goes back to
Second.__init__which then printsSecond(): exiting - execution goes back to
First.__init__which then printsFirst(): exiting - execution goes back to
Third.__init__which then printsThird(): exiting
- 根据MRO
Third.__init__执行。- 印刷
Third(): entering - 然后
super(Third, self).__init__()执行并返回First.__init__被调用的MRO 。
- 印刷
First.__init__执行。- 印刷
First(): entering - 然后
super(First, self).__init__()执行并返回Second.__init__被调用的MRO 。
- 印刷
Second.__init__执行。- 印刷
Second(): entering - 然后
super(Second, self).__init__()执行并返回object.__init__被调用的MRO 。
- 印刷
object.__init__执行(那里的代码中没有打印语句)- 执行返回
Second.__init__,然后打印Second(): exiting - 执行返回
First.__init__,然后打印First(): exiting - 执行返回
Third.__init__,然后打印Third(): exiting
This details out why instantiating Third() results in to :
这详细说明了为什么实例化 Third() 会导致:
Third(): entering
First(): entering
Second(): entering
Second(): exiting
First(): exiting
Third(): exiting
The MRO algorithm has been improved from Python 2.3 onwards to work well in complex cases, but I guess that using the "depth-first left-to-right traversal" + "removing duplicates expect for the last" still works in most cases (please comment if this is not the case). Be sure to read the blog post by Guido!
MRO 算法已经从 Python 2.3 开始改进,可以在复杂情况下很好地工作,但我想使用“深度优先从左到右遍历”+“去除最后一个重复”在大多数情况下仍然有效(请如果不是这种情况,请发表评论)。请务必阅读 Guido 的博客文章!
回答by Seraj Ahmad
class First(object):
def __init__(self, a):
print "first", a
super(First, self).__init__(20)
class Second(object):
def __init__(self, a):
print "second", a
super(Second, self).__init__()
class Third(First, Second):
def __init__(self):
super(Third, self).__init__(10)
print "that's it"
t = Third()
Output is
输出是
first 10
second 20
that's it
Call to Third() locates the initdefined in Third. And call to super in that routine invokes initdefined in First. MRO=[First, Second]. Now call to super in initdefined in First will continue searching MRO and find initdefined in Second, and any call to super will hit the default object init. I hope this example clarifies the concept.
调用 Third() 定位在 Third 中定义的init。在该例程中调用 super 会调用First 中定义的init。MRO=[第一,第二]。现在,在 First中定义的init 中调用 super将继续搜索 MRO 并找到在 Second 中定义的init,并且对 super 的任何调用都将命中默认对象init。我希望这个例子澄清了这个概念。
If you don't call super from First. The chain stops and you will get the following output.
如果你不从 First 调用 super。链停止,您将获得以下输出。
first 10
that's it
回答by Marco Sulla
About @calfzhou's comment, you can use, as usually, **kwargs:
关于@calfzhou 的评论,您可以像往常一样使用**kwargs:
class A(object):
def __init__(self, a, *args, **kwargs):
print("A", a)
class B(A):
def __init__(self, b, *args, **kwargs):
super(B, self).__init__(*args, **kwargs)
print("B", b)
class A1(A):
def __init__(self, a1, *args, **kwargs):
super(A1, self).__init__(*args, **kwargs)
print("A1", a1)
class B1(A1, B):
def __init__(self, b1, *args, **kwargs):
super(B1, self).__init__(*args, **kwargs)
print("B1", b1)
B1(a1=6, b1=5, b="hello", a=None)
Result:
结果:
A None
B hello
A1 6
B1 5
You can also use them positionally:
您还可以按位置使用它们:
B1(5, 6, b="hello", a=None)
but you have to remember the MRO, it's really confusing.
但你必须记住 MRO,这真的很令人困惑。
I can be a little annoying, but I noticed that people forgot every time to use *argsand **kwargswhen they override a method, while it's one of few really useful and sane use of these 'magic variables'.
我可能有点烦人,但我注意到人们在每次使用*args和**kwargs重写方法时都忘记了,而这是对这些“神奇变量”的少数真正有用和明智的使用之一。
回答by supi
I would like to add to what @Visionscaper saysat the top:
我想补充@Visionscaper在顶部所说的内容:
Third --> First --> object --> Second --> object
In this case the interpreter doesnt filter out the object class because its duplicated, rather its because Second appears in a head position and doesnt appear in the tail position in a hierarchy subset. While object only appears in tail positions and is not considered a strong position in C3 algorithm to determine priority.
在这种情况下,解释器不会过滤掉对象类,因为它是重复的,而是因为 Second 在层次结构子集中出现在头部位置而不出现在尾部位置。而对象只出现在尾部位置,在 C3 算法中不被认为是确定优先级的强位置。
The linearisation(mro) of a class C, L(C), is the
类 C 的线性化 (mro) L(C) 是
- the Class C
- plus the merge of
- linearisation of its parents P1, P2, .. = L(P1, P2, ...) and
- the list of its parents P1, P2, ..
- C类
- 加上合并
- 其父项 P1, P2, .. = L(P1, P2, ...) 的线性化和
- 其父项 P1、P2、..
Linearised Merge is done by selecting the common classes that appears as the head of lists and not the tail since order matters(will become clear below)
线性化合并是通过选择显示为列表头部而不是尾部的公共类来完成的,因为顺序很重要(下面将变得清晰)
The linearisation of Third can be computed as follows:
Third 的线性化可以计算如下:
L(O) := [O] // the linearization(mro) of O(object), because O has no parents
L(First) := [First] + merge(L(O), [O])
= [First] + merge([O], [O])
= [First, O]
// Similarly,
L(Second) := [Second, O]
L(Third) := [Third] + merge(L(First), L(Second), [First, Second])
= [Third] + merge([First, O], [Second, O], [First, Second])
// class First is a good candidate for the first merge step, because it only appears as the head of the first and last lists
// class O is not a good candidate for the next merge step, because it also appears in the tails of list 1 and 2,
= [Third, First] + merge([O], [Second, O], [Second])
// class Second is a good candidate for the second merge step, because it appears as the head of the list 2 and 3
= [Third, First, Second] + merge([O], [O])
= [Third, First, Second, O]
Thus for a super() implementation in the following code:
因此,对于以下代码中的 super() 实现:
class First(object):
def __init__(self):
super(First, self).__init__()
print "first"
class Second(object):
def __init__(self):
super(Second, self).__init__()
print "second"
class Third(First, Second):
def __init__(self):
super(Third, self).__init__()
print "that's it"
it becomes obvious how this method will be resolved
很明显如何解决这个方法
Third.__init__() ---> First.__init__() ---> Second.__init__() --->
Object.__init__() ---> returns ---> Second.__init__() -
prints "second" - returns ---> First.__init__() -
prints "first" - returns ---> Third.__init__() - prints "that's it"

