在python中继承和覆盖__init__


129

我正在阅读“深入Python”,并在有关类的章节中给出了以下示例:

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

然后,作者说,如果要覆盖该__init__方法,则必须__init__使用正确的参数显式调用父方法。

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

3
请注意,重载是与重载不同的概念。
Dana the Sane,

Answers:


158

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

如今看起来像这样:

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

请注意以下几点:

  1. 我们可以直接继承内建类,如dictlisttuple,等。

  2. super函数负责跟踪此类的超类并在其中适当地调用函数。


5
我应该寻找更好的书/教程吗?
liewl

2
因此,在多重继承的情况下,super()是否代表您全部跟踪它们?
Dana

实际上是dict .__ init __(self),但没什么问题-super(...)调用只是提供了更一致的语法。(我不确定它如何用于多重继承,我认为它可能只会找到一个超类init
David Z

4
super()的意图是它处理多重继承。缺点是,在实践中多重继承仍然很容易中断(请参阅< fuhm.net/super-harmful)。
某事

2
是的,如果有多个继承并且基类采用了构造函数参数,则通常会发现自己手动调用了构造函数。
Torsten Marek

18

在您需要继承的每个类中,您可以运行每个需要在子类启动时进行初始化的类的循环...可以更好地理解可以复制的示例...

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

2
似乎不需要for循环Child.__init__。当我从示例中删除它时,我的孩子仍然打印“祖母”。Parent班级不处理祖父母的初始化吗?
亚当

4
我认为祖父母的init已经由Parent的init处理了,不是吗?
johk95 2014年

15

您实际上不必调用__init__基类的方法,但是通常您希望这样做,因为基类将在其中进行一些重要的初始化,而其余的类方法都需要进行初始化。

对于其他方法,这取决于您的意图。如果只想向基类行为添加某些内容,则需要在自己的代码中另外调用基类方法。如果要从根本上改变行为,则可以不调用基类的方法,而直接在派生类中实现所有功能。


4
为了技术上的完整性,如果您尝试避免调用父级的init,则某些类(例如threading.Thread)将引发巨大的错误。
David Berger

5
我发现所有这些“您不必调用基础的构造函数”的说法都令人非常恼火。您不必用我知道的任何语言来称呼它。通过不初始化成员,它们全部将以几乎相同的方式拧紧(或不拧紧)。建议不要初始化基类在很多方面都是错误的。如果一个类现在不需要初始化,将来它将需要它。构造函数是类结构/语言构造的接口的一部分,应正确使用。正确的用法是在派生的构造函数中的某个时候调用它。这样吧
AndreasT 2013年

2
“在许多方面,建议不初始化基类只是错误的。” 没有人建议不要初始化基类。仔细阅读答案。这与意图有关。1)如果您想保留基类的初始化逻辑,则不要在派生类中重写init方法。2)如果要从基类扩展init逻辑,则定义自己的init方法,然后从中调用基类的init方法。3)如果要替换基类的init逻辑,请定义自己的init方法,而不要从基类中调用该方法。
wombatonfire

4

如果FileInfo类具有多个祖先类,则您绝对应该调用其所有__init __()函数。您还应该对__del __()函数(该函数是析构函数)执行相同的操作。


2

是的,您必须致电__init__每个家长班。如果要覆盖两个父级中都存在的功能,则功能也是如此。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.