【问题标题】:How does a super method work in python in case of multiple inheritance?在多重继承的情况下,超级方法在python中如何工作?
【发布时间】:2014-05-17 17:58:36
【问题描述】:

超级方法在 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 中的 super 如何引用 C 的实例?

【问题讨论】:

  • 我猜 mro 在超级方法的情况下工作方式不同。我认为当从 D 实例调用时,B 中的超级 mro 就像 [B,C,A,object]。
  • Raymond Hettinger 的帖子 super considered super 几乎是这方面的权威指南。
  • 这是一个菱形继承的例子,你“想要”的东西是模棱两可的,无论给定的语言为你做什么,都可能会让你感到惊讶。如果您熟悉 Python MRO,则可以选择默认设置。但是对于显式优于隐式,在D 中重新定义test 并显式调用您想要的任何基础,或者更好地完全重写该方法。 Python 是鸭子类型的,您正在尝试使用经典类型的继承。参考:en.wikipedia.org/wiki/Multiple_inheritance#The_diamond_problem
  • 如果这是经典的类型化继承,则不会导致这种行为。详情请见python-history.blogspot.in/2010/06/method-resolution-order.html

标签: python oop inheritance multiple-inheritance method-resolution-order


【解决方案1】:

来自文档:

超级(类型[,对象或类型]): 返回一个代理对象,该对象将方法调用委托给父类或 sibling 类型的类

强调我的。

MRO(如您打印出来的)确定方法解析顺序,在本例中为(D、B、C、A、对象)。因此,super(B, self)D 中调用时会返回一个代理对象,该对象将根据 MRO 将方法委托给其兄弟C

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

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

【讨论】:

    【解决方案2】:

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

    因此,在上述情况下,当从 D 对象的实例调用 B 内部的 super 时,self 引用 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' 解释: 来自self == [D,B,C,A,object]的MRO以上的(1) 从上面的(2)中,只有那些出现在提供的类(即 C)之后的 MRO 条目将被使用 因此 super 会调用 A 的测试方法。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2012-03-28
      • 2023-01-14
      • 1970-01-01
      • 1970-01-01
      • 2023-03-24
      • 2021-12-20
      • 1970-01-01
      • 2016-07-05
      相关资源
      最近更新 更多