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

时间:2018-04-10 07:11:57

标签: python-3.x inheritance super self

请考虑以下代码示例:使用self.method()super.method()之间的区别是什么。如果class B继承自class A,但尚未覆盖方法p,那么super().p()self.p()不会做同样的事情吗?哪种方式更正确?我假设使用super()将显式调用基类方法,而如果某人覆盖方法p,self.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

2 个答案:

答案 0 :(得分:1)

当前示例中确实没有区别,super().p()self.p()最终都会调用相同的方法。这是因为找不到p类的其他 B属性。

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

当您在pselfsuper().p()上添加另一个self.p()可调用属性(比如说新方法)时,会产生不同的结果。您可以直接在实例或B类上添加此类属性,也可以创建一个继承自C的类B并定义该方法。在所有3个案例中,self.p()会找到新定义,而super()则不会。请注意,self也直接在实例本身上查看属性,而super()仅考虑父类的类属性。

演示在实例上添加属性; 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

直接在类上添加方法进行演示(可以在定义类之后在类上添加方法作为新属性):

>>> 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 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)开始的类层次结构。 super()仅查看方法解析顺序中当前类(定义方法)之后的类(记录在类__mro__属性中)。

不同类别的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__中的每个班级,因此根据示例,搜索将包括CBA以及object的所有内容。

并且p属性位于第二个示例中的B.p,第三个示例中为C.p

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

最常见的假设是,您希望能够覆盖子类中的p,因此对于大多数用例,您必须坚持使用self.p()。< / p>

答案 1 :(得分:0)

正如您自己猜测的那样,如果您的继承类( B )覆盖了您的函数( p ),则只会有所不同。

根据上下文的不同,您可能希望强制类始终使用继承的函数,但是对于通常的实践,我总是会引用自己的函数(因为如果将来它可能会出现这种情况)有一个原因)。