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

为什么Pythondevise者决定,子类的__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' 

Python的__init__和其他语言的构造函数之间的关键区别在于__init__ 不是一个构造函数:它是一个构造函数 (实际的构造函数 (如果有的话,但稍后会看到;-)是__new__并且完全不同)。 当构build所有的超类时(毫无疑问,在“继续”之前这样做是在继续向下构build的)显然是说你正在构造一个子类的实例,这显然不是初始化的情况,因为有许多用例超类的初始化需要被跳过,改变,控制 – 发生,如果有的话,子类初始化的“中间”,等等。

基本上,初始化程序的超类委托在Python中不是自动的,因为完全相同的原因,这样的委托对任何其他方法也不是自动的 – 并且注意那些“其他语言”不会为任何其他方法其他方法… 只是为构造函数(如果适用,析构函数),正如我所提到的,它不是 Python的__init__ 。 ( __new__行为也很奇特,虽然和你的问题没有直接关系,因为__new__是一个特殊的构造函数,它实际上并不一定需要构造任何东西 – 完全可以返回一个已经存在的实例, -instance …显然,Python为您提供了比“其他语言”更好的机制控制,其中包括__new__本身没有自动委托!

当人们鹦鹉说“Python的禅”时,我感到有些尴尬,好像它是任何事情的理由。 这是一个devise理念; 特定的devise决策总是可以用更具体的术语来解释 – 而且必须是,否则“Python的禅”就成为做任何事情的借口。

原因很简单:你不一定要像构造基类一样构造派生类。 你可能有更多的参数,更less的,他们可能是在不同的顺序或根本不相关。

 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") 

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

Java和C ++ 要求基于内存布局来调用基类构造函数。

如果您有一个带有成员field1的类BaseClass ,并且创build了一个新的类SubClass并添加一个成员field2 ,则SubClass一个实例包含field1field2空间。 除非需要所有的inheritance类在自己的构造函数中重复BaseClass的初始化,否则需要BaseClass的构造函数来填充field1 。 如果field1是私有的,那么inheritance类不能初始化field1

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

所以Python子类不要调用它的基类构造函数是非常有意义的。 它可以只是添加属性本身,如果它想。 对于层次结构中的每个类,给定数量的字段没有保留空间,并且BaseClass方法的代码添加的属性与SubClass方法的代码添加的属性之间没有区别。

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

Python通过保持简单来实现这种灵活性。 您通过编写一个__init__方法来初始化对象,该方法在self上设置属性。 而已。 它的行为完全像一个方法,因为它完全是一个方法。 关于必须先做的事情,或者如果你不做其他事情会自动发生的事情,没有其他奇怪和不直观的规则。 它需要服务的唯一目的是在对象初始化期间执行一个钩子来设置初始属性值,并且这样做。 如果你想要做别的事情,你可以在你的代码中明确地写下来。

“明确比隐含更好” 这是同样的推理,表明我们应该明确写“自我”。

我认为最后这是一个好处 – 你能背诵所有关于调用超类的构造函数的规则吗?

子类通常具有不能传递给超类的额外参数。

现在,我们有一个相当长的页面来描述多重inheritance情况下的方法parsing顺序: http : //www.python.org/download/releases/2.3/mro/

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

也许__init__是子类需要覆盖的方法。 有时子类需要父类的函数在添加类特定的代码之前运行,有时需要在调用父类的函数之前设置实例variables。 由于Python不可能知道什么时候调用这些函数是最合适的,所以不应该猜测。

如果这些不影响你,考虑__init__只是另一个function。 如果有问题的函数是dostuff ,你是否还希望Python自动调用父类中的相应函数?

我相信这里一个非常重要的考虑是,通过自动调用super.__init__() ,您可以禁止在调用初始化方法时使用什么参数。 避免自动调用它,并要求程序员明确地执行该调用,这需要很大的灵活性。

毕竟,仅仅因为B类是从A类派生的,并不意味着A.__init__()可以或者应该用与B.__init__()相同的参数来调用。 使调用显式意味着程序员可以例如用完全不同的参数定义B.__init__() ,用这些数据做一些计算,用适当的参数调用A.__init__() ,然后做一些后处理。 如果在B.__init__()之前或之后隐式地从B.__init__()调用A.__init__()那么这种灵活性会很难实现。

为了避免混淆,知道如果child_class没有init ()类,可以调用base_class init ()方法。

例:

 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 pa # prints 5 print qb # prints 0 print za # prints 1 

事实上,当python中的MRO无法在子类中find时,它将在父类中查找init ()。 如果在children类中已经有一个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 za # Error: No attribute named as a can be found.