理解 Python super() 和 __init__() 方法
为什么使用super()
?
使用 Base.__init__
和 super().__init__
之间有区别吗?
class Base(object):
def __init__(self):
print "Base created"
class ChildA(Base):
def __init__(self):
Base.__init__(self)
class ChildB(Base):
def __init__(self):
super(ChildB, self).__init__()
ChildA()
ChildB()
Why is super()
used?
Is there a difference between using Base.__init__
and super().__init__
?
class Base(object):
def __init__(self):
print "Base created"
class ChildA(Base):
def __init__(self):
Base.__init__(self)
class ChildB(Base):
def __init__(self):
super(ChildB, self).__init__()
ChildA()
ChildB()
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(7)
super() 可以让您避免显式引用基类,这很好。 但主要优点是多重继承,其中有各种有趣的东西可以发生。 如果您还没有看过有关 super 的标准文档,请参阅。
请注意,Python 3.0 中的语法已更改:您可以只说
super().__init__()
而不是super(ChildB, self).__init__()
IMO 更好一些。 标准文档还参考了 使用super() 的指南
这是非常有解释性的。super()
lets you avoid referring to the base class explicitly, which can be nice. But the main advantage comes with multiple inheritance, where all sorts of fun stuff can happen. See the standard docs on super if you haven't already.Note that the syntax changed in Python 3.0: you can just say
super().__init__()
instead ofsuper(ChildB, self).__init__()
which IMO is quite a bit nicer. The standard docs also refer to a guide to usingsuper()
which is quite explanatory.我们使用
super
的原因是,可能使用协作多重继承的子类将调用正确的下一个父类函数方法解析顺序 (MRO)。在Python 3中,我们可以这样调用它:
在Python 2中,我们需要使用定义类的名称和
self
来调用super
,但我们会避免从现在开始,因为它是多余的,更慢(由于名称查找),而且更冗长(所以如果你还没有更新你的Python,请更新它!):如果没有 super,你使用多重继承的能力就会受到限制,因为你很难- 转接下一位家长的电话:
我在下面进一步解释。
这段代码的主要区别在于,在
ChildB
中,您在__init__
中使用super 获得了一个间接层
,它使用定义它的类来确定要在 MRO 中查找的下一个类的__init__
。我在规范问题“How to use 'super' in Python?”的答案中说明了这种差异,该问题演示了依赖注入和协作多重继承。
如果 Python 没有
super
下面的代码实际上与
super
非常接近(它是如何在 C 中实现的,减去一些检查和后备行为,然后翻译成 Python):有点像原生 Python:
如果我们没有
super
对象,我们必须在任何地方编写此手动代码(或重新创建它!)以确保我们调用正确的 next 方法方法解析顺序!super 在 Python 3 中如何做到这一点,而无需明确告知调用它的方法是哪个类和实例?
它获取调用堆栈帧,并找到该类(隐式存储为本地自由变量,
__class__
,使调用函数成为该类的闭包)和该函数的第一个参数,该参数应该是通知它使用哪种方法解析顺序 (MRO) 的实例或类。由于它需要 MRO 的第一个参数,因此 将
super
与静态方法一起使用是不可能的无法访问调用它们的类的 MRO。对其他答案的批评:
它相当简单,并没有告诉我们太多信息,但 super 的要点并不是避免编写父类。 重点是确保调用方法解析顺序 (MRO) 中的下一个方法。 这在多重继承中变得很重要。
我在这里解释一下。
让我们创建一个要在 Child 之后调用的依赖项:
现在记住,
ChildB
使用 super,ChildA
不使用:而
UserA
则使用 super不调用 UserDependency 方法:但
UserB
实际上调用 UserDependency 因为ChildB
调用super
:对另一个答案的批评
在任何情况下都不应该这样做下面是另一个答案所暗示的,因为当你对 ChildB 进行子类化时,你肯定会遇到错误:
(这个答案并不聪明或特别有趣,但尽管评论中存在直接批评和超过 17 票反对,但回答者仍然坚持直到一位好心的编辑解决了他的问题。) 说明
:使用
self.__class__
作为在super()
中按名称显式传递类的替代品会导致递归。super
让我们在 MRO 中查找子类的下一个父类(请参阅本答案的第一部分)。 如果我们告诉super
我们在子方法中,它就会查找行中的下一个方法(可能是我们调用它的同一个方法),导致递归,导致逻辑失败(如在回答者的示例中)或超过最大递归深度时的RuntimeError
。幸运的是,Python 3 的新的不带参数的
super()
调用方法允许我们回避这个问题。The reason we use
super
is so that child classes that may be using cooperative multiple inheritance will call the correct next parent class function in the Method Resolution Order (MRO).In Python 3, we can call it like this:
In Python 2, we were required to call
super
like this with the defining class's name andself
, but we'll avoid this from now on because it's redundant, slower (due to the name lookups), and more verbose (so update your Python if you haven't already!):Without super, you are limited in your ability to use multiple inheritance because you hard-wire the next parent's call:
I further explain below.
The primary difference in this code is that in
ChildB
you get a layer of indirection in the__init__
withsuper
, which uses the class in which it is defined to determine the next class's__init__
to look up in the MRO.I illustrate this difference in an answer at the canonical question, How to use 'super' in Python?, which demonstrates dependency injection and cooperative multiple inheritance.
If Python didn't have
super
Here's code that's actually closely equivalent to
super
(how it's implemented in C, minus some checking and fallback behavior, and translated to Python):Written a little more like native Python:
If we didn't have the
super
object, we'd have to write this manual code everywhere (or recreate it!) to ensure that we call the proper next method in the Method Resolution Order!How does super do this in Python 3 without being told explicitly which class and instance from the method it was called from?
It gets the calling stack frame, and finds the class (implicitly stored as a local free variable,
__class__
, making the calling function a closure over the class) and the first argument to that function, which should be the instance or class that informs it which Method Resolution Order (MRO) to use.Since it requires that first argument for the MRO, using
super
with static methods is impossible as they do not have access to the MRO of the class from which they are called.Criticisms of other answers:
It's rather hand-wavey and doesn't tell us much, but the point of
super
is not to avoid writing the parent class. The point is to ensure that the next method in line in the method resolution order (MRO) is called. This becomes important in multiple inheritance.I'll explain here.
And let's create a dependency that we want to be called after the Child:
Now remember,
ChildB
uses super,ChildA
does not:And
UserA
does not call the UserDependency method:But
UserB
does in-fact call UserDependency becauseChildB
invokessuper
:Criticism for another answer
In no circumstance should you do the following, which another answer suggests, as you'll definitely get errors when you subclass ChildB:
(That answer is not clever or particularly interesting, but in spite of direct criticism in the comments and over 17 downvotes, the answerer persisted in suggesting it until a kind editor fixed his problem.)
Explanation: Using
self.__class__
as a substitute for explicitly passing the class by name insuper()
will lead to recursion.super
lets us look up the next parent in the MRO (see the first section of this answer) for child classes. If we tellsuper
we're in the child's method, it will then lookup the next method in line (probably this same one we are calling it from) resulting in recursion, causing either a logical failure (as in the answerer's example) or aRuntimeError
when the maximum recursion depth is exceeded.Python 3's new
super()
calling method with no arguments fortunately allows us to sidestep this issue.值得注意的是,在 Python 3.0+ 中,您可以使用
来进行调用,这很简洁,并且不需要您显式引用父类或类名,这很方便。 我只想补充一点,对于 Python 2.7 或更低版本,有些人通过编写
self.__class__
而不是类名来实现名称不敏感的行为,即,但是,这会中断对
super< 的调用/code> 对于从您的类继承的任何类,其中
self.__class__
可以返回子类。 例如:这里我有一个类
Square
,它是Rectangle
的子类。 假设我不想为 Square 编写单独的构造函数,因为 Rectangle 的构造函数已经足够好了,但无论出于何种原因,我想实现 Square,以便可以重新实现其他一些方法。当我使用
mSquare = Square('a', 10,10)
创建Square
时,Python 会调用Rectangle
的构造函数,因为我还没有' t 给Square
它自己的构造函数。 但是,在Rectangle
的构造函数中,调用super(self.__class__,self)
将返回mSquare
的超类,因此它再次调用 Rectangle 的构造函数。 正如 @S_C 所提到的,这就是无限循环发生的方式。 在这种情况下,当我运行super(...).__init__()
时,我正在调用Rectangle
的构造函数,但由于我没有给它任何参数,所以我会得到一个错误。It's been noted that in Python 3.0+ you can use
to make your call, which is concise and does not require you to reference the parent OR class names explicitly, which can be handy. I just want to add that for Python 2.7 or under, some people implement a name-insensitive behaviour by writing
self.__class__
instead of the class name, i.e.HOWEVER, this breaks calls to
super
for any classes that inherit from your class, whereself.__class__
could return a child class. For example:Here I have a class
Square
, which is a sub-class ofRectangle
. Say I don't want to write a separate constructor forSquare
because the constructor forRectangle
is good enough, but for whatever reason I want to implement a Square so I can reimplement some other method.When I create a
Square
usingmSquare = Square('a', 10,10)
, Python calls the constructor forRectangle
because I haven't givenSquare
its own constructor. However, in the constructor forRectangle
, the callsuper(self.__class__,self)
is going to return the superclass ofmSquare
, so it calls the constructor forRectangle
again. This is how the infinite loop happens, as was mentioned by @S_C. In this case, when I runsuper(...).__init__()
I am calling the constructor forRectangle
but since I give it no arguments, I will get an error.Super 没有副作用
,按预期
进入无限递归。
Super has no side effects
works as expected
gets into infinite recursion.
请注意......使用Python 2.7,我相信自从在版本2.2中引入
super()
以来,你只能调用super()
如果父级之一继承自最终继承object
的类( 新样式类)。就我个人而言,对于 python 2.7 代码,我将继续使用
BaseClassName.__init__(self, args)
直到我真正获得使用super()
的优势。Just a heads up... with Python 2.7, and I believe ever since
super()
was introduced in version 2.2, you can only callsuper()
if one of the parents inherit from a class that eventually inheritsobject
(new-style classes).Personally, as for python 2.7 code, I'm going to continue using
BaseClassName.__init__(self, args)
until I actually get the advantage of usingsuper()
.确实没有。 super() 查看 MRO 中的下一个类(方法解析顺序,通过 cls.__mro__ 访问)来调用方法。 只需调用基
__init__
即可调用基__init__
。 碰巧的是,MRO 只有一件物品——底座。 因此,您确实在做完全相同的事情,但使用 super() 的方式更好(特别是如果您稍后进入多重继承)。There isn't, really.
super()
looks at the next class in the MRO (method resolution order, accessed withcls.__mro__
) to call the methods. Just calling the base__init__
calls the base__init__
. As it happens, the MRO has exactly one item-- the base. So you're really doing the exact same thing, but in a nicer way withsuper()
(particularly if you get into multiple inheritance later).主要区别在于
ChildA.__init__
将无条件调用Base.__init__
而ChildB.__init__
将调用__init__
无论哪个类恰好是self
祖先行中的ChildB
祖先(这可能与您的期望不同)。
如果您添加使用多重继承的
ClassC
:则
Base
不再是ChildB
的父级>ChildC 实例。 现在,如果self
是ChildC
实例,则super(ChildB, self)
将指向Mixin
。您已在
ChildB
和Base
之间插入Mixin
。 您可以通过 super() 来利用它。因此,如果您设计的类可以在协作多重继承场景中使用,那么您可以使用 super,因为您在运行时并不真正知道谁将成为祖先。
超级被视为超级帖子和pycon 2015 随附视频很好地解释了这一点。
The main difference is that
ChildA.__init__
will unconditionally callBase.__init__
whereasChildB.__init__
will call__init__
in whatever class happens to beChildB
ancestor inself
's line of ancestors(which may differ from what you expect).
If you add a
ClassC
that uses multiple inheritance:then
Base
is no longer the parent ofChildB
forChildC
instances. Nowsuper(ChildB, self)
will point toMixin
ifself
is aChildC
instance.You have inserted
Mixin
in betweenChildB
andBase
. And you can take advantage of it withsuper()
So if you are designed your classes so that they can be used in a Cooperative Multiple Inheritance scenario, you use
super
because you don't really know who is going to be the ancestor at runtime.The super considered super post and pycon 2015 accompanying video explain this pretty well.