__init __()是否应该调用父类的__init __()?


132

我在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__()吗?

编辑:有一个非常类似的问题:Python中的继承和重写__init__


您已经大大更改了代码。我能理解原来object是错字。但是现在您甚至没有super提到您的问题的标题。
SilentGhost

我只是以为super被用作父类的名称。我认为没有人会想到该功能。对您的任何误解,我们深表歉意。
乔治Schölly

Answers:


67

在Python中,调用超类__init__是可选的。如果调用它,那么使用super标识符还是显式命名超类也是可选的:

object.__init__(self)

对于对象,由于super方法为空,因此不必严格要求调用super方法。相同__del__

另一方面,对于__new__,您确实应该调用super方法,并将其return用作新创建的对象-除非您明确希望返回其他内容。


因此,没有约定只调用super的实现吗?
乔治Schölly

5
在老式类中,只有在超类实际上定义了init的情况下(通常是没有定义),才可以调用super init。因此,人们通常会考虑调用超级方法,而不是不合原则地进行操作。
Martin v。Löwis09年

1
如果python中的语法像一样简单[super init],它将更加常见。只是一个投机的想法;Python 2.x中的超级构造对我来说有点尴尬。
u0b34a0f6ae 2009年

这似乎是一个有趣的(也可能是矛盾的)例子:bytes.com/topic/python/answers/... 初始化
mlvljr

“可选”的意思是您不必调用它,但是如果您不调用它,它将不会自动被调用。
麦凯

140

如果__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__.


3
必须像这样纠正D(C)类的定义super(D,self).__init__()
eyquem 2011年

11
super().__ init __()仅在Python 3中有效。在Python 2中,您需要super(D,self).__ init __()
Jacinda 2013年

“如果您需要从super的init中获取某些东西……”-这是一个非常有问题的陈述,因为这不是您/子类是否需要“某些东西”的情况,而是基类是否需要某些东西才能有效的情况基类实例并正常工作。作为派生类的实现者,基类内部是您无法/不应该知道的事情,即使您这样做是因为您编写了这两者或内部文档已被记录下来,但将来的基础设计可能会更改,并且由于编写不佳而中断派生类。因此,请始终确保基类已完全初始化。
尼克

105

在Anon的回答中:
“如果__init__除了在当前类中所做的事情之外,还需要从super 进行一些事情__init__,则必须自己调用它,因为这不会自动发生”

令人难以置信:他的措辞与继承原则完全相反。


不是说“ super __init__ (...)中的某事不会自动发生”,而是它会自动发生,但不会发生,因为__init__派生类的定义覆盖了基类。__init__

那么,为什么要定义一个named_class' __init__,因为它会覆盖有人诉诸继承时的目标?

这是因为需要定义一些在基类中未完成的事情__init__,而获得该结果的唯一可能性是将其执行置于派生类的__init__函数中。
换句话说,如果在基类__init____init__没有被覆盖,除了在基类会自动完成的事情外,还需要在基类做些什么。
并非相反。


然后,问题是__init__在实例化时不再激活存在于基类中的所需指令。为了抵消这种失活,需要做一些特殊的事情:显式调用基类' __init__,以便保留基类执行的初始化,而不是添加__init__。这就是官方文档中所说的:

实际上,派生类中的重写方法可能想扩展而不是简单地替换相同名称的基类方法。有一种直接调用基类方法的简单方法:只需调用BaseClassName.methodname(self,arguments)。
http://docs.python.org/tutorial/classes.html#inheritance

这就是全部故事:

  • 当目标是保留基类执行的初始化(即纯继承)时,不需要任何特殊操作,必须避免__init__在派生类中定义一个函数

  • 当目的是替换由基类执行的初始化时,__init__必须在派生类中定义

  • 当目标是将过程添加到由基类执行的初始化时,__init__ 必须定义一个派生类,包括对基类的显式调用__init__


在Anon的职位上,我感到惊讶的不仅是他表达了与继承理论相反的事实,而且还有5个人绕过那个被推崇而又不掉头的家伙,而且在过去的2年中,没有人反应一个线程,其有趣的主题必须相对频繁地阅读。


1
我确定这篇文章将被推荐。恐怕我对原因的解释不多。与分析似乎难以理解的文本相比,拒绝表决更容易。我花了很长时间试图理解Anon的帖子,然后才终于意识到它写的似是而非,没有什么权威性。也许可以将它解释为对继承人了解的权利。但是当有人对继承有不稳定的看法,但这个主题通常不像岩水那么清晰时,我会感到困惑
eyquem 2011年

2
“我确定这篇文章将被投票……”主要问题是您参加聚会有点晚了,大多数人可能只会阅读前几个答案。+1的绝佳解释
Gerrat 2014年

很好的答案是。
Trilarion 2014年

3
您错过了从亚伦引述的句子中的重要单词“另外”。亚伦的说法是完全正确的,并且与您最终所说的相符。
GreenAsJade 2014年

1
这是使python的设计选择有意义的第一个解释。
Joseph Garvin

20

编辑:(在代码更改之后)
我们无法告诉您是否需要调用父母的__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

我从示例中删除了超级调用,是想知道是否应该调用父类的init实现。
乔治Schölly

您可能要编辑标题。但我的回答仍然成立。
SilentGhost

5

没有硬性规定。类的文档应指出子类是否应调用超类方法。有时您想完全替换超类行为,而有时又要增强它-即在超类调用之前和/或之后调用您自己的代码。

更新:相同的基本逻辑适用于任何方法调用。构造函数有时需要特别考虑(因为它们经常设置确定行为的状态)和析构函数,因为它们并行构造函数(例如,在资源分配(例如数据库连接)中)。但是,对于render()小部件的方法可能也是如此。

进一步更新:什么是OPP?你是说OOP吗?否-一个子类经常需要知道一些关于超类的设计。不是内部实现细节-而是超类与其客户(使用类)所拥有的基本契约。这丝毫不违反OOP原则。这就是为什么protected在OOP中通常是一个有效的概念的原因(尽管在Python中当然不是)。


您说过,有时有人想在超类调用之前调用自己的代码。为此,需要了解父类的实现,这会违反OPP。
GeorgSchölly09年

4

海事组织,你应该给它打电话。如果您的超类是object,则不应这样做,但在其他情况下,我认为不调用它是一种例外。正如其他人已经回答的那样,如果您的类甚至不必重写__init__自身,例如在没有(其他)内部状态要初始化的情况下,这将非常方便。


2

是的,您应该始终__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

运行此代码

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.