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

注意以下几点:

    我们可以直接继承内置类,如dictlisttuple

    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__的主要内容,如果未能解决你的问题,请参考以下文章

继承类中的python __init__方法

我是不是必须覆盖 python 的列表函数?

使用 __new__ 覆盖子类中的 __init__

从父类覆盖类方法而不影响下游类

python_基础2

Python继承