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__本身- !).
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__.
Ben*_*Ben 18
Java和C++ 要求由于内存布局而调用基类构造函数.
如果你有一个BaseClass带有成员的类field1,并且你创建了一个SubClass添加成员的新类field2,那么SubClass包含空间的实例为field1和field2.除非需要所有继承类在自己的构造函数中重复初始化,否则需要一个构造函数BaseClass来填充.如果是私有的,那么继承类就无法初始化.field1BaseClassfield1field1
Python不是Java或C++.所有用户定义类的所有实例都具有相同的"形状".它们基本上只是可以插入属性的字典.在完成任何初始化之前,所有用户定义类的所有实例几乎完全相同 ; 它们只是存储尚未存储的属性的地方.
因此,对于不调用其基类构造函数的Python子类来说,它是完全合理的.如果需要,它可以自己添加属性.对于层次结构中的每个类,没有为给定数量的字段保留空间,并且BaseClass方法中的代码添加的属性与方法中的代码添加的属性之间没有区别SubClass.
如果通常情况下SubClass确实希望BaseClass在继续进行自定义之前设置所有的不变量,那么是的,你可以调用BaseClass.__init__()(或使用super,但这很复杂并且有时会有自己的问题).但你没必要.你可以在之前,之后,或者用不同的论点来做.地狱,如果你想要你可以BaseClass.__init__完全从另一种方法调用__init__; 也许你有一些奇怪的懒惰初始化的事情.
Python通过简单易用来实现这种灵活性.通过编写__init__设置属性的方法来初始化对象self.而已.它的行为与方法完全相同,因为它只是一种方法.关于必须首先完成的事情,或者如果你不做其他事情会自动发生的事情,没有其他奇怪和不直观的规则.它需要服务的唯一目的是成为在对象初始化期间执行以设置初始属性值的钩子,并且它就是这样做的.如果您希望它执行其他操作,请在代码中明确地写出它.
Mik*_*iak 10
"明确比隐含更好." 这同样的推理表明我们应该明确地写出"自我".
我认为最终它是一个好处 - 你能否背诵Java关于调用超类构造函数的所有规则?
现在,我们有一个相当长的页面描述了多重继承的方法解析顺序: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)