如何从两个父类调用“重写”方法?

2024-04-23 22:55:55 发布

您现在位置:Python中文网/ 问答频道 /正文

考虑下面的类,其中^ {CD1>}和^ {< CD2>}是父类^ {< CD3>}:

class P1:
    def f(self):
        print('P1')


class P2:
    def f(self):
        print('P2')


class C(P1, P2):
    def f(self):
        print('C')
        # super().f()
        # super(P1, self).f()

c = C()
c.f()

当我运行它时,它会打印C

如果我取消注释第一行super().f(),那么它也将打印P1
因为super()将从直接父级调用该方法,P1

如果我取消第二行的注释,super(P1, self).f(),那么它也会打印P2 因为super(P1, self)将从P1的兄弟P2调用该方法

我想知道的是,如果有任何方法可以通过调用super()函数从父类P1P2调用f方法,而不是像我那样调用两次

或者,有没有其他不使用super函数的方法


2条回答

没有什么好方法可以完全满足你的需求。但是,如果您可以同时修改P1P2,那么您可以实现协作多重继承,您只需要将基类添加到具有该方法的无操作实现的两个父类中:

class GP: # grandparent base class
    def f(self):
        pass  # do nothing

class P1(GP):
    def f(self):
        print("P1")
        super().f()

class P2(GP):
    def f(self):
        print("P2")
        super().f()

class C(P1, P2):
    def f(self):
        print("C")
        super().f()

这是因为super并不完全意味着“我的父类”。它的意思是“此对象的MRO中的下一个”。MRO是方法解析顺序,基本上是在继承中搜索对象的顺序。当在C中调用super()时,它会在P1中找到该方法。但是当在{}(在{}的实例上)中调用{}时,它调用{},因为{}实例的MRO是{}P2位于P1之后,因此它由第二个super调用拾取

需要GP类来结束super调用链。如果没有,最后的super调用将解析为object(这是所有继承树的根),并且由于没有这样的方法,您将得到一个错误。基类中的实现不需要做任何事情,它只需要在最后不调用super

Super从派生类的方法解析顺序(MRO)调用下一个方法

方法f的每个实现都应该调用super,父类不需要相互了解,super将自动调用MRO中的下一个方法

编辑:我忘了mro中的最后一个类始终是object。 对象没有名为f的方法。因此,您应该注意,mro中具有该方法的最后一个类要么不调用super().f,要么捕获AttributeError

只要遵循C3线性化规则,子类就可以更改MRO。这意味着派生类决定哪些代码运行,哪些代码不运行。这是依赖性注射的一种方法

您可以通过_MRO__; atribute检查类的MRO

这个答案主要基于雷蒙德·赫廷格的谈话

class P1:
    def f(self):
        super().f()
        print('P1')

class P2:
    def f(self):
        print('P2')

class C(P1, P2):
    def f(self):
        super().f()
        print('C')

class C2(P2, P1):
    def f(self):
        super().f()
        print('C2')

>>> C().f()
P2
P1
C
>>> C2().f()
P2
C2
>>> C.__mro__
(<class '__main__.C'>, <class '__main__.P1'>, <class '__main__.P2'>, <type 'object'>)
>>> C2.__mro__
(<class '__main__.C2'>, <class '__main__.P2'>, <class '__main__.P1'>, <type 'object'>)

相关问题 更多 >