方法解析顺序(MRO)是Python中的一种机制,确定在多重继承的情况下,搜索方法和属性时类的查找顺序。
C3线性化(C3 linearization)是构造方法解析顺序的算法。理解MRO对于设计复杂类层次结构至关重要,以避免歧义和方法的意外行为。
可以使用__mro__属性或mro()函数来查询任何类的MRO:
class A: def hello(self): print('A') class B(A): def hello(self): print('B') class C(A): def hello(self): print('C') class D(B, C): pass print(D.__mro__) # (<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.A'>, <class 'object'>) obj = D() obj.hello() # 'B', 因为B在MRO中比C靠前
MRO不能显式更改,但可以控制基类的列举顺序。
如果一个类从多个父类继承,方法将以什么顺序被调用?例如:
class Base: def foo(self): print('Base') class Left(Base): def foo(self): print('Left') super().foo() class Right(Base): def foo(self): print('Right') super().foo() class Child(Left, Right): pass Child().foo()
许多人期望“foo”会从Left被调用,然后是Right,然后是Base——确实如此。然而,如果改变基类的顺序(class Child(Right, Left)),顺序将会改变!
正确的答案:
调用顺序将如MRO所定义。在第一种情况下——Left,Right,Base。MRO是通过特定算法(C3线性化)构建的:
[Child, Left, Right, Base, object]
故事
逻辑的多重继承,方法覆盖
开发团队使用多重继承来实现mixins。结果发现,由于基类顺序不正确,混入的通用逻辑被子类“覆盖”,部分业务逻辑未执行,导致数据丢失。
故事
调用了错误的父类方法
在终结方法(__del__)中,预期将调用基类的方法。然而,由于对MRO的错误理解,调用了另一个继承的mixing方法,在这里没有进行资源清理,导致了内存泄漏。
故事
在不理解调用链的情况下使用super()
在Python 3项目中,客户重写了部分多重继承的代码,却忘记更新通过super()的调用。这导致了无限递归的情况,方法不断自我调用,因方法解析顺序的错误而引发。