__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__

python-子类构造函数调用super().__init__()

python 类 四 : 类的继承与覆盖父类方法

python继承