【问题标题】:Inheritance and Overriding __init__ in pythonpython中的继承和覆盖__init__
【发布时间】: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__

  1. 如果FileInfo 类有多个祖先类怎么办?
    • 我必须显式调用所有祖先类的__init__ 方法吗?
  2. 另外,我是否必须对要覆盖的任何其他方法执行此操作?

【问题讨论】:

  • 请注意,重载是一个独立于覆盖的概念。

标签: python overriding superclass


【解决方案1】:

如果 FileInfo 类有多个祖先类,那么您绝对应该调用它们的所有 __init__() 函数。你也应该对 __del__() 函数做同样的事情,它是一个析构函数。

【讨论】:

    【解决方案2】:

    这本书在子类-超类调用方面有点过时了。在子类化内置类方面也有点过时了。

    现在看起来是这样的:

    class FileInfo(dict):
        """store file metadata"""
        def __init__(self, filename=None):
            super(FileInfo, self).__init__()
            self["name"] = filename
    

    注意以下几点:

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

    2. 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__。如果您要覆盖两个父项中都存在的函数,则函数也是如此。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2015-12-12
      • 1970-01-01
      • 2013-10-18
      • 1970-01-01
      • 2017-09-30
      • 2012-01-31
      相关资源
      最近更新 更多