[英]Reason for calling super class constructor using superclass.__init()__ instead of superclass()
I am a beginner in Python and using Lutz's book to understand OOPS in Python. 我是Python的初学者,并且使用Lutz的书来理解Python中的OOPS。 This question might be basic, but I'd appreciate any help. 这个问题可能是基本的,但我将不胜感激。 I researched SO and found answers on "how", but not "why." 我研究了SO,找到了“如何”的答案,但没有找到“为什么”的答案。
As I understand from the book, if Sub
inherits Super
then one need not call superclass' ( Super
's) __init__()
method. 当我从书上知道,如果Sub
继承了Super
然后一个不需要调用父类的( Super
的) __init__()
方法。
Example: 例:
class Super:
def __init__(self,name):
self.name=name
print("Name is:",name)
class Sub(Super):
pass
a = Sub("Harry")
a.name
Above code does assign attribute name
to the object a
. 上面的代码确实将属性name
分配给对象a
。 It also prints the name
as expected. 它还会按预期打印name
。
However, if I modify the code as: 但是,如果我将代码修改为:
class Super:
def __init__(self,name):
print("Inside Super __init__")
self.name=name
print("Name is:",name)
class Sub(Super):
def __init__(self,name):
Super(name) #Call __init__ directly
a = Sub("Harry")
a.name
The above code doesn't work fine. 上面的代码无法正常工作。 By fine, I mean that although Super.__init__()
does get called (as seen from the print statements), there is no attribute attached to a
. 通过罚款,我的意思是,虽然Super.__init__()
不会被调用(从打印语句看到的),没有连接到属性a
。 When I run a.name
, I get an error, AttributeError: 'Sub' object has no attribute 'name'
当我运行a.name
,出现错误, AttributeError: 'Sub' object has no attribute 'name'
I researched this topic on SO, and found the fix on Chain-calling parent constructors in python and Why aren't superclass __init__ methods automatically invoked? 我在SO上对此主题进行了研究,并找到了有关在python中调用链调用父构造函数的修复程序,以及为什么超类__init__方法不会自动调用吗?
These two threads talk about how to fix it, but they don't provide a reason for why. 这两个线程讨论如何修复它,但是没有提供原因。
Question: Why do I need to call Super
's __init__
using Super.__init__(self, name)
OR super(Sub, self).__init__(name)
instead of a direct call Super(name)
? 问题: 为什么我需要使用Super.__init__(self, name)
或super(Sub, self).__init__(name)
而不是直接调用Super(name)
来调用Super
的__init__
?
In Super.__init__(self, name)
and Super(name)
, we see that Super's __init__()
gets called, (as seen from print statements), but only in Super.__init__(self, name)
we see that the attribute gets attached to Sub
class. 在Super.__init__(self, name)
和Super(name)
,我们看到调用了Super的__init__()
(从打印语句中看到),但是仅在Super.__init__(self, name)
我们看到了属性附属于Sub
类。
Wouldn't Super(name)
automatically pass self
(child) object to Super
? Super(name)
不会自动将self
(子)对象传递给Super
吗? Now, you might ask that how do I know that self
is automatically passed? 现在,您可能会问我如何知道self
被自动传递? If I modify Super(name)
to Super(self,name)
, I get an error message that TypeError: __init__() takes 2 positional arguments but 3 were given
. 如果将Super(name)
修改为Super(self,name)
,则会收到一条错误消息,指出TypeError: __init__() takes 2 positional arguments but 3 were given
。 As I understand from the book, self
is automatically passed. 据我了解, self
是自动传递的。 So, effectively, we end up passing self
twice. 因此,有效地,我们最终两次通过self
。
I don't know why Super(name)
doesn't attach name
attribute to Sub
even though Super.__init__()
is run. 我不知道为什么即使Super.__init__()
运行, Super(name)
也不将name
属性附加到Sub
。 I'd appreciate any help. 我将不胜感激。
For reference, here's the working version of the code based on my research from SO: 作为参考,以下是根据我对SO的研究得出的代码的工作版本:
class Super:
def __init__(self,name):
print("Inside __init__")
self.name=name
print("Name is:",name)
class Sub(Super):
def __init__(self,name):
#Super.__init__(self, name) #One way to fix this
super(Sub, self).__init__(name) #Another way to fix this
a = Sub("Harry")
a.name
PS: I am using Python-3.6.5
under Anaconda Distribution. PS:我在Anaconda发行版下使用Python-3.6.5
。
As I understand from the book, if Sub inherits Super then one need not call superclass' (
Super
's)__init__()
method. 当我从书上知道,如果子继承了超级然后一个不需要调用父类的(Super
的)__init__()
方法。
This is misleading. 这是误导。 It's true that you aren't required to call the superclass's __init__
method—but if you don't, whatever it does in __init__
never happens. 的确,您不需要调用超类的__init__
方法,但是,如果不这样做,则__init__
任何操作都不会发生。 And for normal classes, all of that needs to be done. 对于普通班级,所有这些都需要完成。 It is occasionally useful, usually when a class wasn't designed to be inherited from, like this: 有时很有用,通常是在未将类设计为继承自此类的情况下,如下所示:
class Rot13Reader:
def __init__(self, filename):
self.file = open(filename):
def close(self):
self.file.close()
def dostuff(self):
line = next(file)
return codecs.encode(line, 'rot13')
Imagine that you want all the behavior of this class, but with a string rather than a file. 想象一下,您需要此类的所有行为,但要使用字符串而不是文件。 The only way to do that is to skip the open
: 唯一的方法是跳过open
:
class LocalRot13Reader(Rot13Reader):
def __init__(self, s):
# don't call super().__init__, because we don't have a filename to open
# instead, set up self.file with something else
self.file = io.StringIO(s)
Here, we wanted to avoid the self.file
assignment in the superclass. 在这里,我们要避免在超类中分配self.file
。 In your case—as with almost all classes you're ever going to write—you don't want to avoid the self.name
assignment in the superclass. 在你的情况,因为几乎所有的类你曾经打算写你不想要避免self.name
在超分配。 That's why, even though Python allows you to not call the superclass's __init__
, you almost always call it. 这就是为什么即使Python 允许您不调用超类的__init__
,您也几乎总是调用它的原因。
Notice that there's nothing special about __init__
here. 注意,这里的__init__
没有什么特别的。 For example, we can override dostuff
to call the base class's version and then do extra stuff: 例如,我们可以重写dostuff
以调用基类的版本,然后执行其他操作:
def dostuff(self):
result = super().dostuff()
return result.upper()
… or we can override close
and intentionally not call the base class: …或我们可以重写close
并有意不调用基类:
def close(self):
# do nothing, including no super, because we borrowed our file
The only difference is that good reasons to avoid calling the base class tend to be much more common in normal methods than in __init__
. 唯一的区别是,避免调用基类的充分理由在普通方法中比在__init__
更常见。
Question: Why do I need to call
Super's __init__
usingSuper.__init__(self, name)
ORsuper(Sub, self).__init__(name)
instead of a direct callSuper(name)
? 问题:为什么我需要使用Super.__init__(self, name)
或super(Sub, self).__init__(name)
而不是直接调用Super(name)
来调用Super's __init__
?
Because these do very different things. 因为这些功能有很大不同。
Super(name)
constructs a new Super
instance, calls __init__(name)
on it, and returns it to you. Super(name)
构造一个新的Super
实例,在其上调用__init__(name)
,并将其返回给您。 And you then ignore that value. 然后,您将忽略该值。
In particular, Super.__init__
does get called one time either way—but the self
it gets called with is that new Super
instance, that you're just going to throw away, in the Super(name)
case, while it's your own self
in the super(Sub, self).__init__(name)
case. 特别是, Super.__init__
确实会被调用一次,但是调用它的self
是新的Super
实例,在Super(name)
情况下,您只是将其丢弃,而它是您自己的self
在super(Sub, self).__init__(name)
情况下。
So, in the first case, it sets the name
attribute on some other object that gets thrown away, and nobody ever sets it on your object, which is why self.name
later raises an AttributeError
. 因此,在第一种情况下,它将在其他一些被丢弃的对象上设置name
属性,而没有人在您的对象上设置它,这就是为什么self.name
稍后引发AttributeError
。
It might help you understand this if you add something to both class's __init__
methods to show which instance is involved: 如果您在两个类的__init__
方法中都添加了一些内容以显示所涉及的实例,则可能有助于您理解这一点:
class Super:
def __init__(self,name):
print(f"Inside Super __init__ for {self}")
self.name=name
print("Name is:",name)
class Sub(Super):
def __init__(self,name):
print(f"Inside Sub __init__ for {self}")
# line you want to experiment with goes here.
If that last line is super().__init__(name)
, super(Sub, self).__init__name)
, or Super.__init__(self, name)
, you will see something like this: 如果最后一行是super().__init__(name)
, super(Sub, self).__init__name)
或Super.__init__(self, name)
,您将看到以下内容:
Inside Sub __init__ for <__main__.Sub object at 0x10f7a9e80>
Inside Super __init__ for <__main__.Sub object at 0x10f7a9e80>
Notice that it's the same object, the Sub
at address 0x10f7a9e80, in both cases. 请注意,在两种情况下,它都是同一个对象,即地址为0x10f7a9e80的Sub
。
… but if that last line is Super(name)
: …但是如果最后一行是Super(name)
:
Inside Sub __init__ for <__main__.Sub object at 0x10f7a9ea0>
Inside Super __init__ for <__main__.Super object at 0x10f7a9ec0>
Now we have two different objects, at different addresses 0x10f7a9ea0 and 0x10f7a9ec0, and with different types. 现在,我们有两个不同的对象,它们的地址分别为0x10f7a9ea0和0x10f7a9ec0,并且类型不同。
If you're curious about what the magic all looks like under the covers, Super(name)
does something like this (oversimplifying a bit and skipping over some steps 1 ): 如果您对所有魔术的外观都感到好奇,那么Super(name)
执行以下操作(简化一些,并跳过一些步骤1 ):
_newobj = Super.__new__(Super)
if isinstance(_newobj, Super):
Super.__init__(_newobj, name)
… while super(Sub, self).__init__(name)
does something like this: …而super(Sub, self).__init__(name)
则是这样的:
_basecls = magically_find_next_class_in_mro(Sub)
_basecls.__init__(self, name)
As a side note, if a book is telling you to use super(Sub, self).__init__(name)
or Super.__init__(self, name)
, it's probably an obsolete book written for Python 2. 附带说明一下,如果一本书告诉您使用super(Sub, self).__init__(name)
或Super.__init__(self, name)
,则可能是为Python 2写的过时的书。
In Python 3, you just do this: 在Python 3中,您只需执行以下操作:
super().__init__(name)
: Calls the correct next superclass by method resolution order. super().__init__(name)
:按方法解析顺序调用正确的下一个超类。 You almost always want this. 您几乎总是想要这个。 super(Sub, self).__init__(name)
: Calls the correct next superclass—unless you make a mistake and get Sub
wrong there. super(Sub, self).__init__(name)
:调用正确的下一个超类-除非您犯了一个错误并在那里使Sub
错误。 You only need this if you're writing dual-version code that has to run in 2.7 as well as 3.x. 仅当您要编写必须在2.7和3.x中运行的双版本代码时,才需要此代码。 Super.__init__(self, name)
: Calls Super
, whether it's the correct next superclass or not. Super.__init__(self, name)
:调用Super
,无论它是否是正确的下一个超类。 You only need this if the method resolution order is wrong and you have to work around it. 仅当方法解析顺序错误并且需要解决时,才需要此方法。 2 2 If you want to understand more, it's all in the docs, but it can be a bit daunting: 如果您想了解更多,这些都在文档中,但这可能有点令人生畏:
__new__
__init__
super
(also see Raymond Hettinger's blog post ) super
(另请参阅Raymond Hettinger的博客文章 ) The original introduction to super
, __new__
, and all the related features was very helpful to me in understanding all of this. super
, __new__
以及所有相关功能的原始介绍对我理解所有这些非常有帮助。 I'm not sure if it'll be as helpful to someone who's not coming at this already understanding old-style Python classes, but it's pretty well written, and Guido (obviously) knows what he's talking about, so it might be worth reading. 我不确定这是否会对那些已经不了解老式Python类的人有帮助,但是它写得不错,Guido(显然)知道他在说什么,所以值得一读。
1. The biggest cheat in this explanation is that super
actually returns a proxy object that acts like _baseclass
bound to self
in the same way methods are bound, which can be used to bind methods, like __init__
. 1.这种解释中最大的_baseclass
是, super
实际上返回了一个代理对象,该代理对象的行为类似于_baseclass
绑定到self
,其方式与方法绑定的方式相同,可用于绑定诸如__init__
方法。 This is useful/interesting knowledge if you know how methods work, but probably just extra confusion if you don't. 如果您知道方法的工作原理,那么这是有用的/有趣的知识,但如果您不知道方法的话,可能只是额外的困惑。
2. … or if you're working with old-style classes, which don't support super
(or proper method-resolution order). 2.…或如果您正在使用不支持super
(或正确的方法解析顺序)的旧式类。 This never comes up in Python 3, which doesn't have old-style classes. 没有老式类的Python 3永远不会出现这种情况。 But, unfortunately, you will see it in lots of tkinter examples, because the best tutorial is still Effbot's, which was written for Python 2.3, when Tkinter was all old-style classes, and has never been updated. 但是,不幸的是,您会在许多tkinter示例中看到它,因为最好的教程仍然是Effbot,它是为Python 2.3编写的,当时Tkinter都是老式类,并且从未进行过更新。
Super(name)
is not a "direct call" to the superclass __init__
. Super(name)
不是对超类__init__
的“直接调用”。 After all, you called Super
, not Super.__init__
. 毕竟,您叫Super
,而不是Super.__init__
。
Super.__init__
takes an uninitialized Super
instance and initializes it. Super.__init__
接受未初始化的Super
实例并对其进行初始化。 Super
creates and initializes a new, completely separate instance from the one you wanted to initialize (and then you immediately throw the new instance away). Super
创建并初始化了一个与要初始化的实例完全独立的新实例(然后立即将新实例扔掉了)。 The instance you wanted to initialize is untouched. 您要初始化的实例未更改。
Super(name)
instantiates a new instance of super. Super(name)
实例化super的新实例。 Think of this example: 想想这个例子:
def __init__(self, name):
x1 = Super(name)
x2 = Super("some other name")
assert x1 is not self
assert x2 is not self
In order to explicitly call The Super
's constructor on the current instance, you'd have to use the following syntax: 为了在当前实例上显式调用Super
的构造函数,您必须使用以下语法:
def __init__(self, name):
Super.__init__(self, name)
Now, maybe you don't want read further if you are a beginner. 现在,如果您是初学者,也许您不想进一步阅读。
If you do, you will see that there is a good reason to use super(Sub, self).__init__(name)
(or super().__init__(name)
in Python 3) instead of Super.__init__(self, name)
. 如果这样做,您会发现有充分的理由使用super(Sub, self).__init__(name)
(或Python 3中的super().__init__(name)
)而不是Super.__init__(self, name)
。
Super.__init__(self, name)
works fine, as long as you are certain that Super
is in fact your superclass. 只要您确定Super
实际上是您的超类Super.__init__(self, name)
可以正常工作。 But in fact, you don't know ever that for sure. 但实际上,您永远都不知道。
You could have the following code: 您可能具有以下代码:
class Super:
def __init__(self):
print('Super __init__')
class Sub(Super):
def __init__(self):
print('Sub __init__')
Super.__init__(self)
class Sub2(Super):
def __init__(self):
print('Sub2 __init__')
Super.__init__(self)
class SubSub(Sub, Sub2):
pass
You would now expect that SubSub()
ends up calling all of the above constructors, but it does not: 您现在可以期望SubSub()
最终调用上述所有构造函数,但不会:
>>> x = SubSub()
Sub __init__
Super __init__
>>>
To correct it, you'd have to do: 要更正它,您必须执行以下操作:
class Super:
def __init__(self):
print('Super __init__')
class Sub(Super):
def __init__(self):
print('Sub __init__')
super().__init__()
class Sub2(Super):
def __init__(self):
print('Sub2 __init__')
super().__init__()
class SubSub(Sub, Sub2):
pass
Now it works: 现在可以正常工作:
>>> x = SubSub()
Sub __init__
Sub2 __init__
Super __init__
>>>
The reason is that the super class of Sub
is declared to be Super
, in case of multiple inheritance in class SubSub
, Python's MRO establishes the inheritance as: SubSub
inherits from Sub
, which inherits from Sub2
, which inherits from Super
, which inherits from object
. 原因是Sub
SubSub
类被声明为Super
,如果SubSub
类中有多个继承,Python的MRO将继承建立为: SubSub
继承自Sub
, Sub
继承自Sub2
, Sub2
继承自Super
, Super
继承自object
。
You can test that: 您可以测试:
>>> SubSub.__mro__
(<class '__main__.SubSub'>, <class '__main__.Sub'>, <class '__main__.Sub2'>, <class '__main__.Super'>, <class 'object'>)
Now, the super()
call in constructors of each of the classes finds the next class in the MRO so that the constructor of that class can be called. 现在,每个类的构造函数中的super()
调用都会在MRO中找到下一个类,以便可以调用该类的构造函数。
See https://www.python.org/download/releases/2.3/mro/ 参见https://www.python.org/download/releases/2.3/mro/
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.