python3 继承 self.method() 与 super.method()

python3 inheritance self.method() vs super.method()

考虑以下代码示例:使用 self.method()super.method() 之间的区别是什么。如果class B继承自class A,但没有重写方法p,那么super().p()self.p()不也是一样吗?哪种方式更正确?我假设使用 super() 将显式调用基础 class 方法,而 self.p() 可能 运行 如果有人覆盖方法 p 会出现问题。

class A:
    def p(self):
        print("A")

class B(A):
    def c(self):
        self.p()
    def d(self):
        super().p()

>>> b = B()
>>> b.c()
A
>>> b.d()
A

在您的当前示例中确实没有区别,super().p()self.p() 最终都调用了相同的方法。那是因为找不到 other p B class 属性。

哪一个更正确取决于当附加代码引入 p 某处 时您希望发生什么。这可以在实例上,B class 本身(稍后在应用程序开发周期中,由您或其他开发人员),或者在继承自 [=17 的新 class 上=].

当您在 selfsuper().p()self.p() 上添加另一个 p 可调用属性(比如一个新方法)时,将产生不同的结果。你可以直接在实例上添加这样的属性,或者在 B class 上,或者你可以创建一个继承自 B 的 class C 和定义那个方法。在所有 3 种情况下,self.p() 会找到新定义,而 super() 不会。请注意,self 还直接查看 实例本身 上的属性,而 super() 仅考虑父 class 上的 class 属性。

演示在实例上添加一个属性; lambda 只是定义函数的一种简短方式:

>>> b_instance = B()
>>> b_instance.p = lambda: print('p directly defined on the b instance')
>>> b_instance.c()
p directly defined on the b instance
>>> b_instance.d()
A

直接在class上添加方法的Demo(你可以在class上定义后将方法作为新属性添加):

>>> B.p = lambda self: print('The p method defined on B')
>>> b_instance = B()
>>> b_instance.c()
The p method defined on B
>>> b_instance.d()
A
>>> del B.p  # delete the new method again to go back to the original state

带有新 class 的演示:

>>> class C(B):
...     def p(self):
...         print('The p method defined on C')
...
>>> c_instance = C()
>>> c_instance.c()
The p method defined on C
>>> c_instance.d()
A

所以 selfsuper() 之间的区别在于开始搜索属性。 self 首先从 实例本身 开始,然后是从 type(self) 开始的 class 层次结构。 super() 只查看方法解析顺序中 current class(定义方法的地方)之后的 classes(记录在class __mro__ 属性)。

不同 classes 的 MRO 是:

>>> A.__mro__
(<class '__main__.A'>, <class 'object'>)
>>> B.__mro__
(<class '__main__.B'>, <class '__main__.A'>, <class 'object'>)
>>> C.__mro__
(<class '__main__.C'>, <class '__main__.B'>, <class '__main__.A'>, <class 'object'>)

非常简单,因为您没有使用多重继承。

以上例子,self.p()看:

  • 实例的__dict__字典,如果那里存在p键,则调用该值。这是第一个例子。
  • type(instance).__mro__ 中的每个 class,因此根据示例,搜索将包括所有 CBA,然后是 object.

并且 p 属性在第二个示例中的 B.p 处找到,在第三个示例中 C.p 处找到。

但在上面的所有示例中,super().p()搜索只查看B.__mro__,首先定位B,然后从下一个开始class 在列表中,找到 A.p.

最常见的假设是您希望能够在子 class 中覆盖 p,因此对于大多数 use-cases,您会坚持使用 self.p().

正如您自己推测的那样,只有当您的继承 class (B) 覆盖了您的函数 (p).

根据上下文,您可能希望强制 class 始终使用继承的函数,但对于常见的做法,我总是会引用自己的函数(因为如果它将来存在,它可能会存在一个原因)。