代码之家  ›  专栏  ›  技术社区  ›  user3738870 YOUSIF ALHURIYA

python中diamond继承中的super()奇怪行为

  •  3
  • user3738870 YOUSIF ALHURIYA  · 技术社区  · 7 年前

    如下例所示, super()

    class Vehicle:
        def start(self):
            print("engine has been started")
    
    class LandVehicle(Vehicle):
        def start(self):
            super().start()
            print("tires are safe to use")
    
    class WaterCraft(Vehicle):
        def start(self):
            super().start()
            print("anchor has been pulled up")
    
    class Amphibian(LandVehicle, WaterCraft):
        def start(self):
            # we do not want to call WaterCraft.start, amphibious
            # vehicles don't have anchors
            LandVehicle.start(self)
            print("amphibian is ready for travelling on land")
    
    amphibian = Amphibian()
    amphibian.start()
    

    以上代码生成以下输出:

    engine has been started
    anchor has been pulled up
    tires are safe to use
    amphibian is ready for travelling on land
    

    当我打电话的时候 super().some_method() ,我决不会期望调用同一继承级别的类的方法。所以在我的例子中,我不会期望 anchor has been pulled up

    班级呼叫 超级() 可能甚至不知道最终调用其方法的另一个类。在我的例子中, LandVehicle 可能根本不知道 WaterCraft .

    这种行为是正常的/预期的吗?如果是的话,背后的原因是什么?

    1 回复  |  直到 7 年前
        1
  •  3
  •   Blckknght    7 年前

    在Python中使用继承时,每个类都定义了一个方法解析顺序(methodresolution Order,MRO),用于决定在查找类属性时在何处查找。为了你的 Amphibian 类,例如,MRO是 两栖动物 , LandVehicle WaterCraft , Vehicle 最后呢 object . (你可以打电话自己看 Amphibian.mro()

    MRO的具体推导细节有点复杂(不过如果您感兴趣的话,可以找到它是如何工作的描述)。要知道的重要一点是,任何子类总是列在其父类之前,如果正在进行多重继承,则子类的所有父类都将处于相同的相对顺序中 class 语句(其他类可能出现在父类之间,但它们之间永远不会相互反转)。

    super 超级的 调用(即使调用方法的对象是其他派生类)。那什么时候 LandVehicle.__init__ super().__init__ ,它开始检查 __init__ 方法在第一堂课之后 在MRO里,发现 WaterCraft.__init__ .

    两栖动物 名称 陆地车辆 第二:

    class Amphibian(Watercraft, LandVehicle):
        ...
    

    Amphibian.__init__ 电话 陆地车辆。\u初始__ 直接按名称(而不是使用 ),随后 超级的 船艇 因为他们被呼叫的班级已经在MRO中走得更远了。所以剩下的人 电话将按您的预期工作。

    但这并不是一个很好的解决方案。当您显式地这样命名基类时,您可能会发现,如果您有更多的子类希望以不同的方式进行操作,那么它会在以后破坏一些东西。例如,从重新排序的基派生的类 两栖动物 上面提到的可能会以其他基类结束 LandVehcle __初始化__ 两栖动物__ LandVehcle.__init__ 直接。

    更好的解决办法是允许 方法依次被调用,但要排除其中的部分,您可能不希望总是运行到其他可以单独重写的方法中。

    例如,你可以改变 收件人:

    class WaterCraft(Vehicle):
        def start(self):
            super().start()
            self.weigh_anchor()
    
        def weigh_anchor(self):
            print("anchor has been pulled up")
    

    这个 两栖动物 类可以重写特定于锚点的行为(例如,不执行任何操作):

    class Amphibian(LandVehicle, WaterCraft):
        def start(self):
            super().start(self)
            print("amphibian is ready for travelling on land")
    
        def weigh_anchor(self):
            pass # no anchor to weigh, so do nothing
    

    当然在这种情况下 船艇 船艇 作为 两栖动物