超级方法在python中是如何工作的?
在给定的代码中:
class A(object):
def test(self):
return 'A'
class B(A):
def test(self):
return 'B->'+super(B, self).test()
class C(A):
def test(self):
return 'C'
class D(B,C):
pass
print B().test() # B->A
print D().test() # B->C ????
# MRO of classes are as
print 'mro of A', A.__mro__ # [A,object]
print 'mro of B', B.__mro__ # [B,A,object]
print 'mro of C', C.__mro__ # [C,A,object]
print 'mro of D', D.__mro__ # [D,B,C,A,object]
当在 D 上调用 test
时,它会输出B->C
而不是B->A
(这是我所期望的)。
B 内部的超级如何指代 C 的实例?
从文档中:
super(type[, object-or-type]): 返回将方法调用委托给父类或同级类的代理对象
强调我的。
MRO(如您打印出的)确定方法解析顺序,在本例中为 (D, B, C, A, 对象)。因此,super(B, self)
在 D
中调用时返回一个代理对象,该对象将根据 MRO 将方法委托给其同级C
。
这实际上可用于扩展继承层次结构中的行为,而无需修改现有类。请注意,B
实际上并不知道其test()
方法将被调度到哪个类 - 它只是使用间接super()
调用。只需创建一个按所需顺序混合多个基类的新类,即可在 MRO 中插入不同的类。
例如,假设原始层次结构仅包含类 A
、 B
和 D
。假设稍后,您需要使用日志记录来检测B
的所有方法调用。与其修改B
,不如简单地创建一个新的类C
,该类执行日志记录并D
继承C
,从而将其插入 MRO 紧跟在 B 之后。
在一般情况下,super() 接受两个参数:一个类和该类的实例。(1) 实例对象(自身)确定将使用哪个 MRO 来解析任何属性。(2) 提供的类确定该 MRO 的子集,因为 super() 仅使用 MRO 中在提供的类之后发生的那些条目。
因此,在上述情况下,当从D对象的实例调用B内部的super时,自引用D对象(即实例对象),D(即实例对象)的mro是[D,B,C,A],因此根据(2)仅使用MRO中在B之后发生的那些条目。 即 [C,A]
因此,建议的用法是提供使用 super() 作为第一个参数的类,将标准 self 用作第二个参数。生成的对象将保留 self 的实例命名空间字典,但它仅检索在 MRO 中稍后找到的类上定义的属性,而不是提供的类。
因此,如果我们重新定义为
class B(A):
def test(self):
return 'B->'+super(C, self).test()
然后调用 D().test() 将输出 ==> 'B->A'解释:从 (1) 以上自我 MRO == [D,B,C,A,对象]从上面的 (2) 中,只会使用在提供的类(即 C)之后发生的 MRO 条目因此超级将调用 A 的测试方法。