为什么不自动调用超类__init__方法?

jrd*_*oko 146 python inheritance delegation subclass superclass

为什么Python设计者决定子类的__init__()方法不会__init__()像其他语言那样自动调用超类的方法?Pythonic和推荐的成语是否真的如下?

class Superclass(object):
    def __init__(self):
        print 'Do something'

class Subclass(Superclass):
    def __init__(self):
        super(Subclass, self).__init__()
        print 'Do something else'
Run Code Online (Sandbox Code Playgroud)

Ale*_*lli 157

Python的之间至关重要的差别__init__及其它语言的构造是,__init__不是一个构造器:它是一个初始化(实际构造函数(如果有的话,但是,看到后来;-)是__new__和完全不同的作品再次).虽然构建所有超(,毫无疑问,这样做,你继续向下构建"之前")显然是说你的一部分构建一个子类的实例,这显然是不适合的情况下初始化,因为有许多用例需要跳过,更改,控制 - 超级类的初始化,如果有的话,在子类初始化的"中间",等等.

基本上,初始化程序的超类委派在Python中不是自动的,原因完全相同,这种委托对于任何其他方法也不是自动的- 并且请注意那些"其他语言"不会为任何其他方法执行自动超类委派其他方法要么... 只是为构造函数(如果适用,析构函数),正如我所提到的,它不是 Python的__init__.(行为__new__也很奇特,虽然与你的问题没有直接关系,因为它__new__是一个特殊的构造函数,它实际上并不一定需要构造任何东西 - 可以很好地返回现有实例,甚至是非实例......显然Python为你提供了很多更力学比"其他语言"的控制你心目中,这包括具有中没有自动代表团__new__本身- !).

  • -1为"`__init__`不是构造函数......实际的构造函数......是`__new__`".正如你自己所说,`__new__`的行为与其他语言的构造函数完全不同.`__init__`实际上非常相似(在创建新对象期间调用它,**在分配对象后**,在新对象上设置成员变量),并且几乎总是实现功能的地方在其他语言中,您将放入构造函数.所以只需称它为构造函数! (51认同)
  • 事实上,来自http://docs.python.org/reference/datamodel.html#basic-customization的`__init__`的python文档:"作为**构造函数**的特殊约束,不能返回任何值;这样做会导致在运行时引发TypeError"(强调我的).所以,它是官方的,`__ init__`是一个构造函数. (33认同)
  • 我也认为这是一个相当荒谬的陈述:"**构建**所有超类......显然是说你正在构建**子类的实例,这显然不是**初始化**的情况".构造/初始化中没有任何内容可以使任何人"显而易见".并且`__new__`也不会自动调用超类`__new__`.所以你声称关键区别是**构造**必然涉及构造超类,而**初始化**与你声称`__new__`是构造函数不一致. (7认同)
  • Upvoted是因为对我来说,真正的好处是你提到在子类的初始化(或根本不是)中的任何一点调用超类的\ __ init()\ __的能力. (6认同)
  • 在Python/Java术语中,`__ init__`被称为构造函数.此构造函数是在对象完全构造并初始化为包含其最终运行时类型的默认状态后调用的初始化函数.它不等同于C++构造函数,它们在具有未定义状态的静态类型的已分配对象上调用.这些也与`__new__`不同,所以我们至少有四种不同的分配/构造/初始化函数.语言使用混合术语,重要的部分是行为而不是术语. (3认同)
  • “构造函数”与“初始化程序”是一个区别,没有区别。任何OOP语言的任何构造函数的用户编写部分都是初始化。 (2认同)

Gle*_*ard 36

当人们嘲笑"禅宗蟒蛇"时,我有点尴尬,好像这是任何事情的理由.这是一种设计理念; 特定的设计决策总是可以用更具体的术语来解释 - 它们必须是,或者"Python的禅"成为做任何事情的借口.

原因很简单:您不一定以类似于构造基类的方式构造派生类.您可能有更多参数,更少,它们可能处于不同的顺序或根本不相关.

class myFile(object):
    def __init__(self, filename, mode):
        self.f = open(filename, mode)
class readFile(myFile):
    def __init__(self, filename):
        super(readFile, self).__init__(filename, "r")
class tempFile(myFile):
    def __init__(self, mode):
        super(tempFile, self).__init__("/tmp/file", mode)
class wordsFile(myFile):
    def __init__(self, language):
        super(wordsFile, self).__init__("/usr/share/dict/%s" % language, "r")
Run Code Online (Sandbox Code Playgroud)

这适用于所有派生方法,而不仅仅是__init__.

  • 这个例子提供了一些特别的东西 静态语言也可以做到这一点 (6认同)
  • 所以?它如何防止默认(无参数)超类构造函数被隐式调用?这就是其他语言(C++、Java 等)所做的事情。 (2认同)

Ben*_*Ben 18

Java和C++ 要求由于内存布局而调用基类构造函数.

如果你有一个BaseClass带有成员的类field1,并且你创建了一个SubClass添加成员的新类field2,那么SubClass包含空间的实例为field1field2.除非需要所有继承类在自己的构造函数中重复初始化,否则需要一个构造函数BaseClass来填充.如果是私有的,那么继承类就无法初始化.field1BaseClassfield1field1

Python不是Java或C++.所有用户定义类的所有实例都具有相同的"形状".它们基本上只是可​​以插入属性的字典.在完成任何初始化之前,所有用户定义类的所有实例几乎完全相同 ; 它们只是存储尚未存储的属性的地方.

因此,对于不调用其基类构造函数的Python子类来说,它是完全合理的.如果需要,它可以自己添加属性.对于层次结构中的每个类,没有为给定数量的字段保留空间,并且BaseClass方法中的代码添加的属性与方法中的代码添加的属性之间没有区别SubClass.

如果通常情况下SubClass确实希望BaseClass在继续进行自定义之前设置所有的不变量,那么是的,你可以调用BaseClass.__init__()(或使用super,但这很复杂并且有时会有自己的问题).但你没必要.你可以在之前,之后,或者用不同的论点来做.地狱,如果你想要你可以BaseClass.__init__完全从另一种方法调用__init__; 也许你有一些奇怪的懒惰初始化的事情.

Python通过简单易用来实现这种灵活性.通过编写__init__设置属性的方法来初始化对象self.而已.它的行为与方法完全相同,因为它只是一种方法.关于必须首先完成的事情,或者如果你不做其他事情会自动发生的事情,没有其他奇怪和不直观的规则.它需要服务的唯一目的是成为在对象初始化期间执行以设置初始属性值的钩子,并且它就是这样做的.如果您希望它执行其他操作,请在代码中明确地写出它.

  • 至于C++,它与“内存布局”无关。与 Python 提供的相同的初始化模型可以用 C++ 实现。构造/销毁是它们在 C++ 中的唯一原因是因为设计决策为资源管理 (RAII) 提供可靠且行为良好的设施,这也可以自动生成(意味着更少的代码和人为错误)由编译器,因为它们的规则(它们的调用顺序)是严格定义的。不确定,但 Java 很可能只是简单地遵循这种方法成为另一种类似 POLA C 的语言。 (2认同)
  • 我认为这是一个 F-ed 的选择,但 +1 可以连贯地解释它。 (2认同)

Mik*_*iak 10

"明确比隐含更好." 这同样的推理表明我们应该明确地写出"自我".

我认为最终它是一个好处 - 你能否背诵Java关于调用超类构造函数的所有规则?

  • 我在很大程度上同意你的意见,但Java的规则实际上非常简单:除非你特别要求另一个构造函数,否则调用no-arg构造函数. (8认同)
  • 如果你试图明确地调用它会发生完全相同的事情. (8认同)
  • @Laurence - 当父类没有定义无参数构造函数时会发生什么?当无参数构造函数受保护或私有时会发生什么? (2认同)

Joh*_*ooy 8

通常,子类具有额外的参数,这些参数无法传递给超类.


vir*_*tor 7

现在,我们有一个相当长的页面描述了多重继承的方法解析顺序:http://www.python.org/download/releases/2.3/mro/

如果自动调用构造函数,则需要另一个至少具有相同长度的页面来解释发生的顺序.那将是地狱......


小智 5

为了避免混淆,知道__init__()child_class没有__init__()类时可以调用base_class 方法是很有用的。

例:

class parent:
  def __init__(self, a=1, b=0):
    self.a = a
    self.b = b

class child(parent):
  def me(self):
    pass

p = child(5, 4)
q = child(7)
z= child()

print p.a # prints 5
print q.b # prints 0
print z.a # prints 1
Run Code Online (Sandbox Code Playgroud)

实际上,__init__()当在子类中找不到它时,python中的MRO会在父类中查找。如果子类中已经有一个__init__()方法,则需要直接调用父类的构造函数。

例如,以下代码将返回错误:class parent:def init(self,a = 1,b = 0):self.a = a self.b = b

    class child(parent):
      def __init__(self):
        pass
      def me(self):
        pass

    p = child(5, 4) # Error: constructor gets one argument 3 is provided.
    q = child(7)  # Error: constructor gets one argument 2 is provided.

    z= child()
    print z.a # Error: No attribute named as a can be found.
Run Code Online (Sandbox Code Playgroud)