‘super’ 是什么意思? 用Python做? - super().__init__() 和显式超类 __init__() 之间的区别
之间有什么区别?
class Child(SomeBaseClass):
def __init__(self):
super(Child, self).__init__()
:和:
class Child(SomeBaseClass):
def __init__(self):
SomeBaseClass.__init__(self)
我已经看到 super
在只有单一继承的类中被大量使用。 我可以理解为什么您会在多重继承中使用它,但不清楚在这种情况下使用它的优点是什么。
这个问题是关于技术实现细节以及访问基类__init__
方法的不同方式之间的区别。 要关闭 OP 只是缺少 super
调用并询问为什么基类属性不可用的重复问题,请使用 为什么我的子类实例不包含基类的属性(当我尝试使用它们时导致 AttributeError)? 相反。
What's the difference between:
class Child(SomeBaseClass):
def __init__(self):
super(Child, self).__init__()
and:
class Child(SomeBaseClass):
def __init__(self):
SomeBaseClass.__init__(self)
I've seen super
being used quite a lot in classes with only single inheritance. I can see why you'd use it in multiple inheritance but am unclear as to what the advantages are of using it in this kind of situation.
This question is about technical implementation details and the distinction between different ways of accessing the base class __init__
method. To close duplicate questions where OP is simply missing a super
call and is asking why base class attributes aren't available, please use Why don't my subclass instances contain the attributes from the base class (causing an AttributeError when I try to use them)? instead.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(11)
有什么不同?
意思是调用
SomeBaseClass
的__init__
。 while表示从父类调用绑定的
__init__
,该父类遵循实例的方法解析顺序 (MRO) 中SomeBaseClass
的子类(定义此方法的子类)。如果实例是这个子类的子类,则 MRO 中接下来可能有不同的父类。
简单解释
当您编写一个类时,您希望其他类能够使用它。
super()
使其他类更容易使用您正在编写的类。正如 Bob Martin 所说,良好的架构可以让您尽可能推迟决策。
super() 可以实现这种架构。
当另一个类继承您编写的类时,它也可以从其他类继承。 根据方法解析的类的顺序,这些类可能有一个位于
__init__
之后的__init__
。如果没有 super,您可能会硬编码您正在编写的类的父级(就像示例中那样)。 这意味着您不会调用 MRO 中的下一个 __init__ ,因此您将无法重用其中的代码。
如果您编写自己的代码供个人使用,您可能不关心这种区别。 但是,如果您希望其他人使用您的代码,那么使用
super
可以为代码用户提供更大的灵活性。Python 2 与 3
这适用于 Python 2 和 3:
这只适用于 Python 3:
它通过在堆栈帧中向上移动并获取方法的第一个参数(通常是
self
)来在没有参数的情况下工作实例方法或类方法的 cls - 但也可以是其他名称)并在自由变量中查找类(例如Child
)(使用名称进行查找)__class__
作为方法中的自由闭包变量)。我以前更喜欢演示使用
super
的交叉兼容方式,但现在 Python 2 已基本弃用,我将演示 Python 3 的处理方式,即调用super
没有参数。具有前向兼容性的间接
它会给您带来什么? 对于单一继承,从静态分析的角度来看,问题中的示例实际上是相同的。 但是,使用
super
为您提供了一个具有前向兼容性的间接层。前向兼容性对于经验丰富的开发人员来说非常重要。 您希望您的代码在更改时能够以最小的更改继续工作。 当您查看修订历史记录时,您希望准确了解何时更改了哪些内容。
您可以从单一继承开始,但是如果您决定添加另一个基类,您只需更改基类的行 - 如果您继承的类中的基类发生变化(例如添加了 mixin),您就需要更改这堂课什么也没有。
在 Python 2 中,获取
super
的参数和正确的方法参数可能会有点混乱,因此我建议使用仅 Python 3 的调用方法。如果您知道自己在单继承中正确使用了
super
,那么接下来的调试就会变得更加困难。依赖注入
其他人可以使用您的代码并将父级注入到方法解析中:
假设您向对象添加另一个类,并且想要在 Foo 和 Bar 之间注入一个类(用于测试或其他原因):
使用非超级子级无法注入依赖项,因为您使用的子项已硬编码要在其自己之后调用的方法:
但是,具有使用
super
的子项的类可以正确注入依赖项:评论
Python 通过 C3 线性化算法 对复杂的继承树进行线性化,以创建方法解析顺序 (MRO)。
我们希望按该顺序查找方法。
对于在父级中定义的方法,要在没有
super
的情况下按该顺序查找下一个方法,它必须UnsuperChild
无法访问InjectMe
。UnsuperInjector
可以访问InjectMe
- 但无法从它从UnsuperChild
继承的方法中调用该类的方法。两个 Child 类都打算调用 MRO 中接下来出现的同名方法,这可能是它在创建时不知道的另一个类。
没有 super 的方法会硬编码其父类的方法 - 因此限制了其方法的行为,并且子类无法在调用链中注入功能。
带有
super
的具有更大的灵活性。 可以拦截方法的调用链并注入功能。您可能不需要该功能,但代码的子类可能需要。
结论
始终使用
super
来引用父类,而不是对其进行硬编码。您的目的是引用下一行的父类,而不是您看到的子类继承的父类。
不使用
super
可能会给代码的用户带来不必要的限制。What's the difference?
means to call
SomeBaseClass
's__init__
. whilemeans to call a bound
__init__
from the parent class that followsSomeBaseClass
's child class (the one that defines this method) in the instance's Method Resolution Order (MRO).If the instance is a subclass of this child class, there may be a different parent that comes next in the MRO.
Explained simply
When you write a class, you want other classes to be able to use it.
super()
makes it easier for other classes to use the class you're writing.As Bob Martin says, a good architecture allows you to postpone decision making as long as possible.
super()
can enable that sort of architecture.When another class subclasses the class you wrote, it could also be inheriting from other classes. And those classes could have an
__init__
that comes after this__init__
based on the ordering of the classes for method resolution.Without
super
you would likely hard-code the parent of the class you're writing (like the example does). This would mean that you would not call the next__init__
in the MRO, and you would thus not get to reuse the code in it.If you're writing your own code for personal use, you may not care about this distinction. But if you want others to use your code, using
super
is one thing that allows greater flexibility for users of the code.Python 2 versus 3
This works in Python 2 and 3:
This only works in Python 3:
It works with no arguments by moving up in the stack frame and getting the first argument to the method (usually
self
for an instance method orcls
for a class method - but could be other names) and finding the class (e.g.Child
) in the free variables (it is looked up with the name__class__
as a free closure variable in the method).I used to prefer to demonstrate the cross-compatible way of using
super
, but now that Python 2 is largely deprecated, I will demonstrate the Python 3 way of doing things, that is, callingsuper
with no arguments.Indirection with Forward Compatibility
What does it give you? For single inheritance, the examples from the question are practically identical from a static analysis point of view. However, using
super
gives you a layer of indirection with forward compatibility.Forward compatibility is very important to seasoned developers. You want your code to keep working with minimal changes as you change it. When you look at your revision history, you want to see precisely what changed when.
You may start off with single inheritance, but if you decide to add another base class, you only have to change the line with the bases - if the bases change in a class you inherit from (say a mixin is added) you'd change nothing in this class.
In Python 2, getting the arguments to
super
and the correct method arguments right can be a little confusing, so I suggest using the Python 3 only method of calling it.If you know you're using
super
correctly with single inheritance, that makes debugging less difficult going forward.Dependency Injection
Other people can use your code and inject parents into the method resolution:
Say you add another class to your object, and want to inject a class between Foo and Bar (for testing or some other reason):
Using the un-super child fails to inject the dependency because the child you're using has hard-coded the method to be called after its own:
However, the class with the child that uses
super
can correctly inject the dependency:Addressing a comment
Python linearizes a complicated inheritance tree via the C3 linearization algorithm to create a Method Resolution Order (MRO).
We want methods to be looked up in that order.
For a method defined in a parent to find the next one in that order without
super
, it would have toThe
UnsuperChild
does not have access toInjectMe
. It is theUnsuperInjector
that has access toInjectMe
- and yet cannot call that class's method from the method it inherits fromUnsuperChild
.Both Child classes intend to call a method by the same name that comes next in the MRO, which might be another class it was not aware of when it was created.
The one without
super
hard-codes its parent's method - thus is has restricted the behavior of its method, and subclasses cannot inject functionality in the call chain.The one with
super
has greater flexibility. The call chain for the methods can be intercepted and functionality injected.You may not need that functionality, but subclassers of your code may.
Conclusion
Always use
super
to reference the parent class instead of hard-coding it.What you intend is to reference the parent class that is next-in-line, not specifically the one you see the child inheriting from.
Not using
super
can put unnecessary constraints on users of your code.单继承中
super()
的好处是微乎其微的——大多数情况下,您不必将基类的名称硬编码到使用其父方法的每个方法中。但是,如果没有
super()
,则几乎不可能使用多重继承。 这包括常见的习惯用法,如 mixins、接口、抽象类等。这会扩展到稍后扩展您的代码。 如果后来有人想编写一个扩展Child
和 mixin 的类,他们的代码将无法正常工作。The benefits of
super()
in single-inheritance are minimal -- mostly, you don't have to hard-code the name of the base class into every method that uses its parent methods.However, it's almost impossible to use multiple-inheritance without
super()
. This includes common idioms like mixins, interfaces, abstract classes, etc. This extends to code that later extends yours. If somebody later wanted to write a class that extendedChild
and a mixin, their code would not work properly.我玩了一下
super()
,并且认识到我们可以更改调用顺序。例如,我们有下一个层次结构:
在这种情况下 D 的 MRO 将是(仅适用于 Python 3):
让我们创建一个类其中
super()
在方法执行后调用。所以我们可以看到解析顺序与 MRO 中的相同。 但是当我们在方法的开头调用 super() 时:
我们有不同的顺序,它与 MRO 元组的顺序相反。
对于其他阅读,我会推荐下一个答案:
I had played a bit with
super()
, and had recognized that we can change calling order.For example, we have next hierarchy structure:
In this case MRO of D will be (only for Python 3):
Let's create a class where
super()
calls after method execution.So we can see that resolution order is same as in MRO. But when we call
super()
in the beginning of the method:We have a different order it is reversed a order of the MRO tuple.
For additional reading I would recommend next answers:
这一切不是都假设基类是新式类吗?
在Python 2中不起作用。
class A
必须是new-style,即:class A(object)
Doesn't all of this assume that the base class is a new-style class?
Will not work in Python 2.
class A
must be new-style, i.e:class A(object)
当调用 super() 解析为类方法、实例方法或静态方法的父级版本时,我们希望将我们所在范围的当前类作为第一个参数传递,以指示哪个父级的范围我们试图解析感兴趣的对象作为第二个参数,以指示我们尝试将该范围应用于哪个对象。
考虑一个类层次结构
A
、B
和C
,其中每个类都是其后一个类的父类,并且a、
b
和c
各自的实例。将
super
与静态方法一起使用,例如 在
__new__()
方法中使用super()
解释:
1- 尽管
__new__()
通常将其作为第一个参数对调用类的引用,它在 Python 中不是作为类方法实现的,而是作为静态方法实现的。 也就是说,直接调用 __new__() 时,必须显式传递对类的引用作为第一个参数:2- 调用
super()
获取父级时class 我们传递子类A
作为其第一个参数,然后传递对感兴趣对象的引用,在本例中,它是在A.__new__(cls) 时传递的类引用
被调用。 在大多数情况下,它也恰好是对子类的引用。 在某些情况下可能不是,例如在多代继承的情况下。3- 由于一般规则 __new__() 是静态方法,因此
super(A, cls).__new__ 也将返回静态方法,并且需要显式提供所有参数,包括对感兴趣的对象的引用,在本例中是
cls
。4-在没有
super
的情况下做同样的事情将
super
与实例方法一起使用,例如 在
__init__()
中使用super()
说明:
1-
__init__
是一个实例方法,这意味着它采用一个引用作为其第一个参数到一个实例。 当直接从实例调用时,引用是隐式传递的,也就是说您不需要指定它:2-当在
__init__()
中调用super()
时,我们将子类作为第一个参数传递,将感兴趣的对象作为第二个参数传递,该对象通常是对子类实例的引用。3- 调用
super(A, self)
返回一个代理,该代理将解析范围并将其应用于self
,就好像它现在是父类的实例一样。 我们将该代理称为s
。 由于__init__()
是一个实例方法,因此调用s.__init__(...)
将隐式传递self
的引用作为第一个参数到父级的__init__()
。4- 要在没有
super
的情况下执行相同的操作,我们需要将对实例的引用显式传递给父版本的__init__()
。将
super
与类方法一起使用说明:
1- 可以从类中直接调用类方法,并将对该类的引用作为其第一个参数。
2-当在类方法中调用
super()
来解析其父类的版本时,我们希望将当前子类作为第一个参数传递,以指示我们尝试解析到哪个父类的范围,以及感兴趣的对象作为第二个参数,指示我们想要将该范围应用于哪个对象,该对象通常是对子类本身或其子类之一的引用。3- 调用
super(B, cls)
解析为A
的范围并将其应用于cls
。 由于alternate_constructor()
是一个类方法,因此调用super(B, cls).alternate_constructor(...)
将隐式传递cls
的引用作为A
的alternate_constructor()
版本的第一个参数4- 要在不使用
super()
的情况下执行相同操作,您需要获取对A.alternate_constructor()
的未绑定版本的引用(即函数的显式版本)。 简单地这样做是行不通的:上面的方法行不通,因为
A.alternate_constructor()
方法将对A
的隐式引用作为其第一个参数。 此处传递的 cls 将是其第二个参数。When calling
super()
to resolve to a parent's version of a classmethod, instance method, or staticmethod, we want to pass the current class whose scope we are in as the first argument, to indicate which parent's scope we're trying to resolve to, and as a second argument the object of interest to indicate which object we're trying to apply that scope to.Consider a class hierarchy
A
,B
, andC
where each class is the parent of the one following it, anda
,b
, andc
respective instances of each.Using
super
with a staticmethode.g. using
super()
from within the__new__()
methodExplanation:
1- even though it's usual for
__new__()
to take as its first param a reference to the calling class, it is not implemented in Python as a classmethod, but rather a staticmethod. That is, a reference to a class has to be passed explicitly as the first argument when calling__new__()
directly:2- when calling
super()
to get to the parent class we pass the child classA
as its first argument, then we pass a reference to the object of interest, in this case it's the class reference that was passed whenA.__new__(cls)
was called. In most cases it also happens to be a reference to the child class. In some situations it might not be, for instance in the case of multiple generation inheritances.3- since as a general rule
__new__()
is a staticmethod,super(A, cls).__new__
will also return a staticmethod and needs to be supplied all arguments explicitly, including the reference to the object of insterest, in this casecls
.4- doing the same thing without
super
Using
super
with an instance methode.g. using
super()
from within__init__()
Explanation:
1-
__init__
is an instance method, meaning that it takes as its first argument a reference to an instance. When called directly from the instance, the reference is passed implicitly, that is you don't need to specify it:2- when calling
super()
within__init__()
we pass the child class as the first argument and the object of interest as a second argument, which in general is a reference to an instance of the child class.3- The call
super(A, self)
returns a proxy that will resolve the scope and apply it toself
as if it's now an instance of the parent class. Let's call that proxys
. Since__init__()
is an instance method the calls.__init__(...)
will implicitly pass a reference ofself
as the first argument to the parent's__init__()
.4- to do the same without
super
we need to pass a reference to an instance explicitly to the parent's version of__init__()
.Using
super
with a classmethodExplanation:
1- A classmethod can be called from the class directly and takes as its first parameter a reference to the class.
2- when calling
super()
within a classmethod to resolve to its parent's version of it, we want to pass the current child class as the first argument to indicate which parent's scope we're trying to resolve to, and the object of interest as the second argument to indicate which object we want to apply that scope to, which in general is a reference to the child class itself or one of its subclasses.3- The call
super(B, cls)
resolves to the scope ofA
and applies it tocls
. Sincealternate_constructor()
is a classmethod the callsuper(B, cls).alternate_constructor(...)
will implicitly pass a reference ofcls
as the first argument toA
's version ofalternate_constructor()
4- to do the same without using
super()
you would need to get a reference to the unbound version ofA.alternate_constructor()
(i.e. the explicit version of the function). Simply doing this would not work:The above would not work because the
A.alternate_constructor()
method takes an implicit reference toA
as its first argument. Thecls
being passed here would be its second argument.Super() 简而言之
示例
这个小示例涵盖了所有有趣的情况:
调用的确切顺序由调用方法的实例决定:
例如 a,没有超级调用:
例如 b< /em>,祖先链为
B -> A-> object
:例如c,祖先链是
C -> A-> object
:例如d,祖先链更有趣
D -> B-> C-> A-> object
(mro 代表方法解析顺序):更多信息
回答了“super 在 Python 中做什么?”的问题后,下一个问题是如何有效地使用它。 请参阅此分步教程或此45 分钟视频。
Super() in a nutshell
Example
This small example covers all the interesting cases:
The exact order of calls is determined by the instance the method is called from:
For instance a, there is no super call:
For instance b, the ancestor chain is
B -> A -> object
:For instance c, the ancestor chain is
C -> A -> object
:For instance d, the ancestor chain is more interesting
D -> B -> C -> A -> object
(mro stands for method resolution order) :More information
Having answered the question of "What does super do in Python?", the next question is how to use it effectively. See this step-by-step tutorial or this 45 minute video.
许多很好的答案,但对于视觉学习者来说:
首先让我们探索一下 super 的参数,然后没有。
假设有一个从
Jack
类创建的实例jack
,它具有如图中绿色所示的继承链。 调用:super(Jack, jack).method(...)
会使用
jack
的MRO(方法解析顺序)(其继承树按一定顺序),并将从Jack
开始搜索。 为什么可以提供一个父类呢? 好吧,如果我们从实例jack
开始搜索,它会找到实例方法,重点是找到它的父方法。如果不向 super 提供参数,则就像传入的第一个参数是 self 的类,传入的第二个参数是 self 。 这些是在 Python3 中自动为您计算的。
然而,假设我们不想使用
Jack
的方法,我们可以传入Jen
来开始向上搜索,而不是传入Jack
来自Jen
的方法。它一次搜索一层(宽度而不是深度),例如,如果
Adam
和Sue
都具有所需的方法,则来自Sue
的方法将首先被找到。如果
Cain
和Sue
都有所需的方法,则首先调用Cain
的方法。这在代码中对应于:
MRO是从左到右。
Many great answers, but for visual learners:
Firstly lets explore with arguments to super, and then without.
Imagine theres an instance
jack
created from the classJack
, who has the inheritance chain as shown in green in the picture. Calling:super(Jack, jack).method(...)
will use the MRO (Method Resolution Order) of
jack
(its inheritance tree in a certain order), and will start searching fromJack
. Why can one provide a parent class? Well if we start searching from the instancejack
, it would find the instance method, the whole point is to find its parents method.If one does not supply arguments to super, its like the first argument passed in is the class of
self
, and the second argument passed in isself
. These are auto-calculated for you in Python3.However say we dont want to use
Jack
's method, instead of passing inJack
, we could of passed inJen
to start searching upwards for the method fromJen
.It searches one layer at a time (width not depth), e.g. if
Adam
andSue
both have the required method, the one fromSue
will be found first.If
Cain
andSue
both had the required method,Cain
's method would be called first.This corresponds in code to:
MRO is from left to right.
在多重继承的情况下,您通常需要调用两个父级的初始化程序,而不仅仅是第一个。 super() 并不总是使用基类,而是查找方法解析顺序 (MRO) 中的下一个类,并将当前对象作为该类的实例返回。 例如:
将
Base.__init__(self)
替换为
super().__init__()
会产生所需的结果。
In the case of multiple inheritance, you normally want to call the initializers of both parents, not just the first. Instead of always using the base class, super() finds the class that is next in Method Resolution Order (MRO), and returns the current object as an instance of that class. For example:
results in
Replacing
Base.__init__(self)
withsuper().__init__()
results inas desired.
这里有一些很好的答案,但它们没有解决在层次结构中的不同类具有不同签名的情况下如何使用 super() 的问题......特别是在 __init__ 的情况下>
要回答该部分并能够有效地使用
super()
我建议阅读我的答案 super() 并更改协作方法的签名。这只是这种情况的解决方案:
用法示例:
输出:
some great answers here, but they do not tackle how to use
super()
in the case where different classes in the hierarchy have different signatures ... especially in the case of__init__
to answer that part and to be able to effectively use
super()
i'd suggest reading my answer super() and changing the signature of cooperative methods.here's just the solution to this scenario:
example usage:
output:
考虑以下代码:
如果将
Y
的构造函数更改为X.__init__
,您将得到:但是使用
super(Y, self).__init__(),你会得到:
并且
P
或Q
甚至可能涉及到另一个文件,而你在编写X
时并不知道这个文件,并且Y
。 因此,基本上,当您编写class Y(X)
时,您不会知道super(Child, self)
将引用什么,即使 Y 的签名如下简单如Y(X)
。 这就是为什么 super 可能是更好的选择。Consider the following code:
If change constructor of
Y
toX.__init__
, you will get:But using
super(Y, self).__init__()
, you will get:And
P
orQ
may even be involved from another file which you don't know when you writingX
andY
. So, basically, you won't know whatsuper(Child, self)
will reference to when you are writingclass Y(X)
, even the signature of Y is as simple asY(X)
. That's why super could be a better choice.这很容易理解。
好的,如果您使用 super(Child,self) 现在会发生什么?
当一个Child实例被创建时,它的MRO(方法解析顺序)根据继承的顺序是(Child,SomeBaseClass,object)的顺序。 (假设 SomeBaseClass 除了默认对象之外没有其他父对象)
通过传递
Child, self
,super
在self
的 MRO 中搜索实例,并返回 Child 的下一个代理对象,在本例中为 SomeBaseClass,该对象然后调用 SomeBaseClass 的 __init__ 方法。 换句话说,如果是super(SomeBaseClass,self)
,则super
返回的代理对象将是object
对于多重继承,MRO 可以包含许多类,因此基本上
super
可以让您决定在 MRO 中从哪里开始搜索。This is fairly easy to understand.
Ok, what happens now if you use
super(Child,self)
?When a Child instance is created, its MRO(Method Resolution Order) is in the order of (Child, SomeBaseClass, object) based on the inheritance. (assume SomeBaseClass doesn't have other parents except for the default object)
By passing
Child, self
,super
searches in the MRO of theself
instance, and return the proxy object next of Child, in this case it's SomeBaseClass, this object then invokes the__init__
method of SomeBaseClass. In other word, if it'ssuper(SomeBaseClass,self)
, the proxy object thatsuper
returns would beobject
For multi inheritance, the MRO could contain many classes, so basically
super
lets you decide where you want to start searching in the MRO.