8

超级方法如何在 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]

test在 D 上调用时,它输出B->C而不是B->A(我期望的)。

B 内部的 super 如何引用 C 的实例?

4

2 回答 2

2

从文档:

super(type[, object-or-type]):返回一个代理对象,它将方法调用委托给类型的父类或同级

强调我的。

MRO(如您打印的那样)确定方法解析顺序,在本例中为(D、B、C、A、对象)。因此,super(B, self)当在内部调用时,将返回一个代理对象,该对象将基于 MROD将方法委托给它的兄弟。C

这实际上可以用于扩展继承层次结构中的行为,而无需修改现有类。请注意,B它实际上并不知道它的方法将被分派到哪个类test()——它只是使用间接super()调用。您可以通过简单地创建一个以所需顺序混合多个基类的新类在 MRO 中插入不同的类。

例如,假设您的原始层次结构仅包含ABD。假设稍后,您需要B使用日志记录来检测 's 的所有方法调用。B您可以简单地创建一个C执行日志记录并具有D继承的新类,而不是进行修改C,从而将其插入到 B 之后的 MRO 中。

于 2014-04-05T10:47:45.393 回答
1

通常情况下, super() 有两个参数:一个类和该类的一个实例。(1) 实例对象 (self) 确定将使用哪个 MRO 来解析任何属性。(2) 提供的类确定该 MRO 的子集,因为 super() 仅使用 MRO 中出现在提供的类之后的那些条目。

因此,在上述情况下,当从 D 对象的实例调用 B 内部的超级时,自身引用 D 对象(即实例对象)并且 D 的 mro(即实例对象)是 [D,B,C,A] 因此根据(2 ) 只有在 MRO 中出现在 B 之后的那些条目才会被使用。即[C,A]

因此,推荐的用法是提供 super() 用作第一个参数的类,标准 self 作为第二个参数。生成的对象将保留 self 的实例命名空间字典,但它仅检索在 MRO 中稍后找到的类上定义的属性,而不是提供的类。

因此,如果我们将 B 重新定义为

class B(A):
    def test(self):
        return 'B->'+super(C, self).test()

然后调用 D().test() 将输出 ==> 'B->A' 解释:从上面的 (1) 的 self == [D,B,C,A,object] 上面的 (2) 中只有那些将使用在提供的类(即 C)之后出现的 MRO 条目,因此 super 将调用 A 的测试方法。

于 2014-04-05T11:08:27.960 回答