__init__() 应该调用父类的 __init__() 吗?
Posted
技术标签:
【中文标题】__init__() 应该调用父类的 __init__() 吗?【英文标题】:Should __init__() call the parent class's __init__()? 【发布时间】:2010-11-26 00:36:19 【问题描述】:我在 Objective-C 中使用过这个结构:
- (void)init
if (self = [super init])
// init class
return self;
Python 是否也应该为 __init__
调用父类的实现?
class NewClass(SomeOtherClass):
def __init__(self):
SomeOtherClass.__init__(self)
# init class
对于__new__()
和__del__()
,这也是真/假吗?
编辑:有一个非常相似的问题:Inheritance and Overriding __init__
in Python
【问题讨论】:
您对代码进行了重大更改。我可以理解原来的object
是一个错字。但是现在您甚至没有 super
您的问题所指的标题。
我只是认为 super 用作父类的名称。我认为没有人会想到这个功能。如有任何误解,我深表歉意。
一个为什么不自动超级调用问题:***.com/questions/3782827/…
【参考方案1】:
编辑:(代码更改后)
我们无法告诉您是否需要拨打您父母的__init__
(或任何其他功能)。如果没有这样的调用,继承显然会起作用。这完全取决于您的代码逻辑:例如,如果您的所有__init__
都在父类中完成,您可以完全跳过子类__init__
。
考虑以下示例:
>>> class A:
def __init__(self, val):
self.a = val
>>> class B(A):
pass
>>> class C(A):
def __init__(self, val):
A.__init__(self, val)
self.a += val
>>> A(4).a
4
>>> B(5).a
5
>>> C(6).a
12
【讨论】:
我从示例中删除了 super 调用,我想知道是否应该调用父类的 init 实现。 您可能想要编辑标题。但我的答案仍然有效。【参考方案2】:没有硬性规定。类的文档应该指出子类是否应该调用超类方法。有时您想完全替换超类行为,而在其他时候增强它 - 即在超类调用之前和/或之后调用您自己的代码。
更新:相同的基本逻辑适用于任何方法调用。构造函数有时需要特殊考虑(因为它们经常设置决定行为的状态)和析构函数,因为它们与构造函数并行(例如在资源分配中,例如数据库连接)。但同样的情况也可能适用于小部件的 render()
方法。
进一步更新:什么是 OPP?你是说OOP吗?不 - 子类通常需要了解有关超类设计的一些信息。不是内部实现细节——而是超类与其客户(使用类)的基本合同。这不会以任何方式违反 OOP 原则。这就是为什么protected
在一般的 OOP 中是一个有效的概念(当然,在 Python 中不是)。
【讨论】:
您说有时需要在调用超类之前调用自己的代码。为此,需要了解父类的实现,这将违反 OPP。【参考方案3】:在 Python 中,调用超类'__init__
是可选的。如果你调用它,那么是否使用super
标识符,或者是否显式命名超类也是可选的:
object.__init__(self)
在对象的情况下,调用超级方法并不是绝对必要的,因为超级方法是空的。 __del__
也一样。
另一方面,对于__new__
,您确实应该调用 super 方法,并将其返回用作新创建的对象 - 除非您明确想要返回不同的东西。
【讨论】:
所以没有约定只调用 super 的实现? 在旧式类中,只有在超类实际上定义了 init 时才能调用超 init(它通常不会t)。因此,人们通常会考虑调用超级方法,而不是违反原则。 如果python中的语法像[super init]
一样简单,那就更常见了。只是一个推测性的想法; Python 2.x 中的 super 结构对我来说有点尴尬。
这似乎是一个有趣(并且可能自相矛盾)的例子:bytes.com/topic/python/answers/…init
"可选",因为你不必调用它,但如果你不调用它,它不会被自动调用。【参考方案4】:
IMO,你应该打电话给它。如果你的超类是object
,你不应该,但在其他情况下,我认为不调用它是例外的。正如其他人已经回答的那样,如果您的类甚至不必重写 __init__
本身,例如当它没有(额外的)内部状态要初始化时,这将非常方便。
【讨论】:
【参考方案5】:如果除了当前班级的__init__,
中正在执行的操作之外,您还需要从super 的__init__
中完成某些操作,您必须自己调用它,因为这不会自动发生。但是,如果您不需要来自 super 的 __init__,
的任何内容,则无需调用它。示例:
>>> class C(object):
def __init__(self):
self.b = 1
>>> class D(C):
def __init__(self):
super().__init__() # in Python 2 use super(D, self).__init__()
self.a = 1
>>> class E(C):
def __init__(self):
self.a = 1
>>> d = D()
>>> d.a
1
>>> d.b # This works because of the call to super's init
1
>>> e = E()
>>> e.a
1
>>> e.b # This is going to fail since nothing in E initializes b...
Traceback (most recent call last):
File "<pyshell#70>", line 1, in <module>
e.b # This is going to fail since nothing in E initializes b...
AttributeError: 'E' object has no attribute 'b'
__del__
是同样的方式,(但要小心依赖 __del__
来完成 - 考虑通过 with 语句来代替)。
我很少使用__new__.
我在__init__.
中进行所有初始化
【讨论】:
D(C)类的定义必须像这样更正super(D,self).__init__()
super().__init__() 仅适用于 Python 3。在 Python 2 中,您需要 super(D, self).__init__()
"If you need something from super's init..." - 这是一个非常有问题的陈述,因为它不是你/子类是否需要“某物”的情况",但基类是否需要某些东西才能成为有效的基类实例并正常工作。作为派生类的实现者,基类内部是您不能/不应该知道的事情,即使您这样做是因为您编写了两者或内部已记录,基类的设计可能会在未来发生变化并因编写不当而中断派生类。因此,请始终确保基类已完全初始化。【参考方案6】:
在 Anon 的回答中:“如果除了当前班级的 __init__
中正在做的事情之外,您还需要从 super 的 __init__
中完成某些事情,您必须自己调用它,因为那会不会自动发生”
太不可思议了:他的措辞与继承的原则完全相反。
并不是说 “来自 super 的 __init__
(...) 的事情不会自动发生” ,而是它会自动发生,但它不会发生,因为基础- class' __init__
被派生类的定义覆盖 __init__
那么,为什么要定义一个派生类'__init__
,因为它会覆盖当有人诉诸继承时的目标?
这是因为需要定义一些未在基类'__init__
中完成的东西,而获得它的唯一可能是将其执行放在派生类'__init__
函数中。
换句话说,除了在基类'__init__
中自动完成的工作之外,还需要基类'__init__
中的某些内容(如果后者未被覆盖)。
不是相反。
然后,问题是基类'__init__
中存在的所需指令在实例化时不再被激活。为了抵消这种失活,需要一些特殊的东西:显式调用基类'__init__
,以便KEEP,而不是添加由基类'@987654334执行的初始化@ .
这正是官方文档中所说的:
派生类中的重写方法实际上可能想要扩展 而不是简单地替换同名的基类方法。 有一个简单的方法可以直接调用基类方法:只需 调用 BaseClassName.methodname(self, arguments)。http://docs.python.org/tutorial/classes.html#inheritance
故事就是这样:
当目标是保持基类执行的初始化时,即纯继承,不需要什么特别的,必须避免在派生类中定义__init__
函数
当目标是替换基类执行的初始化时,必须在派生类中定义__init__
当目标是向基类执行的初始化添加进程时,必须定义派生类'__init__
,包括对基类__init__
的显式调用
我在Anon的帖子中感到惊讶的是,他不仅表达了与继承理论相反的观点,而且有5个人路过那个人,连头发都点赞了,而且没有人对此做出反应。 2 年从事主题必须相对经常阅读的主题。
【讨论】:
我确信这篇文章会被点赞。恐怕我不会对原因有很多解释。投反对票比分析看起来难以理解的文本更容易。我花了很长时间试图理解 Anon 的帖子,直到我终于意识到它写得似是而非,而且不是很权威。也许对于了解继承的人来说,这可以解释为大致正确;但是当有人对继承有不稳定的概念时,我会感到困惑,因为这个主题通常不像岩水那么清晰 “我确信这篇文章会被点赞......” 主要问题是你参加聚会有点晚了,大多数人可能不会阅读前几个答案。顺便说一句很好的解释+1 您在引用 Aaron 的句子中遗漏了重要的单词“in addition”。 Aaron 的陈述完全正确,与您最终所说的相符。 这是让python的设计选择有意义的第一个解释。【参考方案7】:是的,您应该始终显式调用基类__init__
作为一种良好的编码习惯。忘记这样做可能会导致微妙的问题或运行时错误。即使__init__
不带任何参数也是如此。这与编译器会为您隐式调用基类构造函数的其他语言不同。 Python 不这样做!
总是调用基类_init__
的主要原因是基类通常可以创建成员变量并将它们初始化为默认值。因此,如果您不调用基类 init,则不会执行任何代码,并且您最终会得到没有成员变量的基类。
示例:
class Base:
def __init__(self):
print('base init')
class Derived1(Base):
def __init__(self):
print('derived1 init')
class Derived2(Base):
def __init__(self):
super(Derived2, self).__init__()
print('derived2 init')
print('Creating Derived1...')
d1 = Derived1()
print('Creating Derived2...')
d2 = Derived2()
这打印出来了..
Creating Derived1...
derived1 init
Creating Derived2...
base init
derived2 init
Run this code.
【讨论】:
以上是关于__init__() 应该调用父类的 __init__() 吗?的主要内容,如果未能解决你的问题,请参考以下文章
Python3基础 super层层调用父类的__init__方法 子类的__init__覆盖了父类的__init__的解决方法
Python3基础 super 子类调用父类的__init__
Python3基础 super 子类调用父类的__init__