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 上=].
当您在 self
、super().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
所以 self
和 super()
之间的区别在于开始搜索属性。 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,因此根据示例,搜索将包括所有 C
、B
、A
,然后是 object
.
并且 p
属性在第二个示例中的 B.p
处找到,在第三个示例中 C.p
处找到。
但在上面的所有示例中,super().p()
搜索只查看B.__mro__
,首先定位B
,然后从下一个开始class 在列表中,找到 A.p
.
最常见的假设是您希望能够在子 class 中覆盖 p
,因此对于大多数 use-cases,您会坚持使用 self.p()
.
正如您自己推测的那样,只有当您的继承 class (B) 覆盖了您的函数 (p).
根据上下文,您可能希望强制 class 始终使用继承的函数,但对于常见的做法,我总是会引用自己的函数(因为如果它将来存在,它可能会存在一个原因)。
考虑以下代码示例:使用 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 上=].
当您在 self
、super().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
所以 self
和 super()
之间的区别在于开始搜索属性。 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,因此根据示例,搜索将包括所有C
、B
、A
,然后是object
.
并且 p
属性在第二个示例中的 B.p
处找到,在第三个示例中 C.p
处找到。
但在上面的所有示例中,super().p()
搜索只查看B.__mro__
,首先定位B
,然后从下一个开始class 在列表中,找到 A.p
.
最常见的假设是您希望能够在子 class 中覆盖 p
,因此对于大多数 use-cases,您会坚持使用 self.p()
.
正如您自己推测的那样,只有当您的继承 class (B) 覆盖了您的函数 (p).
根据上下文,您可能希望强制 class 始终使用继承的函数,但对于常见的做法,我总是会引用自己的函数(因为如果它将来存在,它可能会存在一个原因)。