为什么 Python super 在 child 的 init 方法中使用?

Why is Python super used in the child's init method?

根据Python docs super()

is useful for accessing inherited methods that have been overridden in a class.

我知道 super 指的是 parent class 并且它允许您访问 parent 方法。我的问题是为什么人们总是在 child class 的 init 方法中使用 super?我到处都见过它。例如:

class Person:

    def __init__(self, name):
        self.name = name

class Employee(Person):
    def __init__(self, **kwargs):
       super().__init__(name=kwargs['name']) # Here super is being used

    def first_letter(self):
        return self.name[0]

e = Employee(name="John")
print(e.first_letter())

我可以在没有 super 甚至没有 init 方法的情况下完成同样的事情:

class Person:

    def __init__(self, name):
        self.name = name

class Employee(Person):

    def first_letter(self):
        return self.name[0]

e = Employee(name="John")
print(e.first_letter())

后一种代码有缺点吗?对我来说它看起来干净多了。我什至不必使用样板 **kwargs 和 kwargs['argument'] 语法。

我正在使用 Python 3.8。 编辑:这里有一个 nother Whosebug 个问题,其中包含来自在 child 的 init 方法中使用 super 的不同人的代码。我不明白为什么。我最好的猜测是 Python 3.8.

中有一些新内容

child 可能想要做一些与超级 class 不同的事情,或者更可能是额外的事情 - 在这种情况下,child 必须有一个 __init__

调用 super 的 init 意味着您不必 copy/paste(包含维护的所有含义)child 的 class 中的 init,否则将是如果您想在 child init.

中添加一些额外的代码,则需要

但是请注意,如果您使用多重继承(例如调用哪个 super),那么使用 super 的 init 会很复杂,这需要小心。就个人而言,我避免多重继承并将继承保持在最低限度 - 很容易被诱惑创建多个级别的 inheritance/class 层次结构,但我的经验是“保持简单”的方法通常要好得多。

这里使用super的正确方法是两种方法都使用super。您不能假设 Person 是 MRO 中的最后一个(或者至少是 object 之前的倒数第二个)class。

class Person:

    def __init__(self, name, **kwargs):
        super().__init__(**kwargs)
        self.name = name

class Employee(Person):
    # Optional, since Employee.__init__ does nothing
    # except pass the exact same arguments "upstream"
    def __init__(self, **kwargs):
       super().__init__(**kwargs)

    def first_letter(self):
        return self.name[0]

考虑一个 class 定义,例如

class Bar:
    ...

class Foo(Person, Bar):
    ...

Foo 的 MRO 看起来像 [Foo, Person, Bar, object]Person.__init__ 中对 super().__init__ 的调用将调用 Bar.__init__,而不是 object.__init__,并且 Person 无法知道 **kwargs 中的值是否意味着对于 Bar,因此它 必须 传递它们。

后一种代码的潜在缺点是在 Employee class 中没有 __init__ 方法。由于有none,所以调用父class的__init__方法。但是,一旦将 __init__ 方法添加到 Employee class(可能有一些特定于 Employee 的属性需要初始化,例如 id_number),那么 __init__ 父 class 的方法被覆盖且未被调用(除非调用 super.__init__()),然后 Employee 将没有 name 属性。