139

我在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__()对于and ,这也是真/假__del__()吗?

编辑:有一个非常相似的问题:Inheritance and Overriding __init__in Python

4

7 回答 7

154

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

于 2009-09-06T14:51:06.783 回答
122

在 Anon 的回答中: “如果除了当前班级正在做的事情之外,
你还需要从 super 做一些事情,你必须自己调用它,因为这不会自动发生”__init____init__

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


并不是说“来自 super 的东西__init__ (...) 不会自动发生”,而是它会自动发生,但它不会发生,因为__init__派生类的定义覆盖了基类__init__

那么,为什么要定义一个derived_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 年没有人反应。一个主题必须相对经常阅读的主题。

于 2011-08-14T20:31:39.267 回答
70

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

object.__init__(self)

在对象的情况下,调用超级方法并不是绝对必要的,因为超级方法是空的。对__del__.

另一方面,对于__new__,您确实应该调用 super 方法,并将其返回用作新创建的对象 - 除非您明确想要返回不同的东西。

于 2009-09-06T14:18:07.693 回答
23

编辑:(代码更改后)
我们无法告诉您是否需要调用您父母的__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
于 2009-09-06T14:11:23.533 回答
6

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

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

进一步更新:什么是 OPP?你是说OOP吗?不——子类通常需要了解类的设计。不是内部实现细节——而是超类与其客户(使用类)的基本合同。这不会以任何方式违反 OOP 原则。这就是为什么protected通常在 OOP 中是一个有效的概念(当然,在 Python 中不是)。

于 2009-09-06T14:13:22.757 回答
4

IMO,你应该叫它。如果你的超类是object,你不应该,但在其他情况下,我认为不调用它是例外的。正如其他人已经回答的那样,如果您的类甚至不必重写__init__自己,例如当它没有(额外的)内部状态要初始化时,这将非常方便。

于 2009-09-06T14:47:43.030 回答
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

运行此代码

于 2019-04-19T23:26:31.793 回答