python中的继承和覆盖__init__
Posted
技术标签:
【中文标题】python中的继承和覆盖__init__【英文标题】:Inheritance and Overriding __init__ in python 【发布时间】:2010-10-19 17:06:12 【问题描述】:我正在阅读“深入 Python”,并在关于类的章节中给出了以下示例:
class FileInfo(UserDict):
"store file metadata"
def __init__(self, filename=None):
UserDict.__init__(self)
self["name"] = filename
然后作者说,如果你想重写__init__
方法,你必须用正确的参数显式调用父__init__
。
-
如果
FileInfo
类有多个祖先类怎么办?
我必须显式调用所有祖先类的__init__
方法吗?
另外,我是否必须对要覆盖的任何其他方法执行此操作?
【问题讨论】:
请注意,重载是一个独立于覆盖的概念。 【参考方案1】:如果 FileInfo 类有多个祖先类,那么您绝对应该调用它们的所有 __init__()
函数。你也应该对 __del__()
函数做同样的事情,它是一个析构函数。
【讨论】:
【参考方案2】:这本书在子类-超类调用方面有点过时了。在子类化内置类方面也有点过时了。
现在看起来是这样的:
class FileInfo(dict):
"""store file metadata"""
def __init__(self, filename=None):
super(FileInfo, self).__init__()
self["name"] = filename
注意以下几点:
我们可以直接继承内置类,如dict
、list
、tuple
等
super
函数处理跟踪此类的超类并适当地调用其中的函数。
【讨论】:
我应该寻找更好的书/教程吗? 那么在多重继承的情况下,super() 会代表你追踪它们吗? dict.__init__(self),实际上,但这并没有什么问题 - super(...) 调用只是提供了更一致的语法。 (我不确定它如何用于多重继承,我认为它可能只能找到一个超类init) super() 的目的是处理多重继承。缺点是,实际上多重继承仍然很容易中断(参见 fuhm.net/super-harmful)。 是的,如果多重继承和基类采用构造函数参数,您通常会发现自己手动调用构造函数。【参考方案3】:在您需要继承的每个类中,您可以在启动子类时运行每个需要初始化的类的循环...可以复制的示例可能会更好地理解...
class Female_Grandparent:
def __init__(self):
self.grandma_name = 'Grandma'
class Male_Grandparent:
def __init__(self):
self.grandpa_name = 'Grandpa'
class Parent(Female_Grandparent, Male_Grandparent):
def __init__(self):
Female_Grandparent.__init__(self)
Male_Grandparent.__init__(self)
self.parent_name = 'Parent Class'
class Child(Parent):
def __init__(self):
Parent.__init__(self)
#---------------------------------------------------------------------------------------#
for cls in Parent.__bases__: # This block grabs the classes of the child
cls.__init__(self) # class (which is named 'Parent' in this case),
# and iterates through them, initiating each one.
# The result is that each parent, of each child,
# is automatically handled upon initiation of the
# dependent class. WOOT WOOT! :D
#---------------------------------------------------------------------------------------#
g = Female_Grandparent()
print g.grandma_name
p = Parent()
print p.grandma_name
child = Child()
print child.grandma_name
【讨论】:
Child.__init__
中的 for 循环似乎不是必需的。当我从示例中删除它时,我的孩子仍然打印“祖母”。祖父母初始化不是由Parent
类处理吗?
我认为祖父母的 init 已经由 Parent 的 init 处理了,不是吗?【参考方案4】:
您并没有真正有调用基类的__init__
方法,但您通常想要这样做,因为基类会在那里进行一些重要的初始化,这些初始化是其他类方法工作所必需的。
对于其他方法,这取决于您的意图。如果您只想向基类行为添加一些内容,您将需要在您自己的代码中另外调用基类方法。如果您想从根本上改变行为,您可能不会调用基类的方法并直接在派生类中实现所有功能。
【讨论】:
为了技术完整性,如果您试图避免调用父级的 init,某些类(如 threading.Thread)会引发巨大错误。 我发现所有这些“你不必调用基地的构造函数”的说法非常令人讨厌。你不必用我知道的任何语言来称呼它。通过不初始化成员,所有这些都会以几乎相同的方式搞砸(或不搞砸)。建议不要初始化基类在很多方面都是错误的。如果一个类现在不需要初始化,那么它将来会需要它。构造函数是类结构/语言构造接口的一部分,应该正确使用。正确的用法是在派生的构造函数中调用它。就这样吧。 “建议不要初始化基类在很多方面都是错误的。”没有人建议不要初始化基类。仔细阅读答案。这都是关于意图的。 1) 如果你想保持基类的初始化逻辑不变,你不要在派生类中重写 init 方法。 2)如果你想从基类扩展init逻辑,你定义自己的init方法,然后从中调用基类的init方法。 3)如果你想替换基类的初始化逻辑,你定义自己的初始化方法,而不是从基类调用。 @AndreasT retrobump,我知道,但有些语言会强迫你这样做,尤其是 Java。例如,在 Java 中,语言会隐式调用super();
,除非您显式调用它或其他构造函数,否则会产生编译器错误。【参考方案5】:
是的,您必须为每个父类调用__init__
。如果您要覆盖两个父项中都存在的函数,则函数也是如此。
【讨论】:
以上是关于python中的继承和覆盖__init__的主要内容,如果未能解决你的问题,请参考以下文章