【问题标题】:Python MRO - usage of super in multiple inheritancePython MRO - 在多重继承中使用 super
【发布时间】:2019-11-04 23:49:26
【问题描述】:

我有这段代码,其中使用了超级继承和多重继承。 类的结果是:

go A go!
go C go!
go B go!
go D go!

虽然我期望:

go A go!
go B go!
go D go!

根据我的理解:D因为MRO调用B类,因为go是在B中实现的。B类调用其父A的super。A被执行,没关系。然后我希望 B 继续执行,所以这意味着 B 被执行,最后 D 被执行。 但这当然是不正确的。为什么它进入C,因为go方法的定义在B中找到,然后它不应该再在C中搜索。这就是MRO的工作原理。它发现在头等舱,它不应该再搜索了。完全糊涂了:(

 class A(object):
        def go(self):
            print("go A go!")

class B(A):
    def go(self):
        super(B, self).go()
        print("go B go!")

class C(A):
    def go(self):
        super(C, self).go()
        print("go C go!")

class D(B,C):
    def go(self):
        super(D, self).go()
        print("go D go!")

a = A()
b = B()
c = C()
d = D()

d.go()

【问题讨论】:

  • 我认为搜索方法的类列表是在执行任何方法之前构建的。您似乎假设此列表是在执行方法时动态确定的。

标签: python python-3.x super method-resolution-order


【解决方案1】:

Raymond Hettinger(核心 Python 开发人员)here 的 PyCon 上有一段精彩的视频。

主要的收获是 super 不叫你的父母,它叫你后代的祖先。因此,每当您调用super 时,它都会返回到您实际调用该方法的实例并在 MRO 中查找下一个类型(即使具有多重继承也是保证顺序,为什么class D(B,C) 不是同class D(C, B))。

在您的情况下,D 的 MRO 是 (D,B,C,A)。当D.go 调用super 时,它调用B.go。当它调用 super 时,它仍在使用 D 的 MRO,因此它移动到下一个类型,在你的情况下 C 又调用 A。最终结果是你的打印语句在 D 的 MRO 中执行反转。

There is also a blog by RH here 如果您无法观看 youtube,也会涵盖相同的主题。

【讨论】:

    【解决方案2】:

    当您调用 d.go() 时,对 super 的每次调用都使用调用实例的 MRO(在本例中,d 的 MRO 为 D、B、C、A 对象。super 确实 不一定指静态出现的类的父类。

    这在B.go 的定义中最为明显。尽管B的定义对C一无所知,但它的self参数是对D类型对象的引用,而D的MRO中B之后的下一个类是@987654332 @,不是A

    【讨论】:

      【解决方案3】:

      我个人不经常使用继承并避免多重继承,除非在极少数情况下。

      正如预期的那样,这更棘手。

      我认为如果您先打印然后调用super 稍微更改代码,您会更好地理解事情是如何工作的:

      唯一真正的区别是,我先打印然后调用super。 除此之外,我只使用惰性 python3 语法(无需显式从object 继承,如果从方法中调用,则无需将参数传递给super(),它们将自动正确填写),但这并不什么都改变不了。

      class A:
              def go(self):
                  print("go A go!")
      
      class B(A):
          def go(self):
              print("go B go!")
              super().go()
      
      class C(A):
          def go(self):
              print("go C go!")
              super().go()
      
      class D(B,C):
          def go(self):
              print("go D go!")
              super().go()
      
      b = B()
      print(b.__class__)
      print(b.__class__.__mro__)
      d = D()
      print(d.__class__)
      print(d.__class__.__mro__)
      d.go()
      

      所以您首先看到的是 bd 的类和 MRO,这不足为奇。

      如果D有一个方法,它将被调用,如果它没有在B中寻找方法,如果不在C中,如果不在A

      因此D.go() 将首先被调用。

      D.go() 调用的super(D, self).go() 将在self.__class__.__mro__ 的MRO 中查找下一个条目。记得我们在D,所以它会查看B,它会在其中找到go 方法并调用它。

      现在事情的行为与您期望的不同,B 中的 go() 方法不像您期望的那样查看 B 的 MRO,它继续查看 @ 的 MRO 中的下一个条目987654342@,这是C,而不是你所期望的A,因为self.__class__当然仍然是D

      希望这有助于理解。

      上述脚本的输出将是:

      <class '__main__.B'>
      (<class '__main__.B'>, <class '__main__.A'>, <class 'object'>)
      <class '__main__.D'>
      (<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.A'>, <class 'object'>)
      go D go!
      go B go!
      go C go!
      go A go!
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2020-11-27
        • 2016-02-25
        • 1970-01-01
        • 1970-01-01
        • 2020-03-20
        • 1970-01-01
        • 2020-07-05
        相关资源
        最近更新 更多