Python3菱形继承结构中super()为何指向右分支类而非顶层父类?
为什么Python菱形继承中super()指向了非直接父类?
你遇到的这个问题本质是Python**方法解析顺序(MRO)**在多继承场景下的工作逻辑——super()并不是简单指向当前类的直接父类,而是按照类的MRO列表寻找下一个匹配方法的类。咱们结合你的代码一步步拆解,就能明白背后的原因。
首先先看你的简化代码:
class Geometry_0(): @classmethod def x2y(cls,geom_pars: dict, x: float) -> float: print('in Geometry_0') return NotImplemented class Geometry(Geometry_0): @classmethod def transform(cls,geom_pars: dict, x_child: float) ->float: return NotImplemented @classmethod def x2y(cls,geom_pars: dict, x_child: float): print('in Geometry') x_parent = cls.transform(geom_pars,x_child) return super().x2y(geom_pars,x_parent) # 这里的super()指向了Parabola而非Geometry_0? class Parabola(Geometry_0): # y = a * x**2 @classmethod def x2y(cls, geom_pars, x): print('in Parabola') a = geom_pars['a'] return a * x**2 class Parabola_shifted(Geometry,Parabola): # y = a * (x-x0)**2 @classmethod def transform(cls,geom_pars: dict, x_child: float) ->float: x0 = geom_pars['x0'] x_parent = x_child - x0 return x_parent print('parabola') print(Parabola.x2y({'a': 1},2)) print('shifted parabola') print(Parabola_shifted.x2y({'a':1, 'x0': 2}, 2))
核心原因:Python的MRO机制
Python在处理多继承时,会为每个类生成一个线性的继承顺序列表(可以通过类名.__mro__查看),super()的调用就是严格按照这个列表来寻找下一个要执行的方法,而不是只看当前类的直接父类。
我们先打印Parabola_shifted的MRO:
print(Parabola_shifted.__mro__)
输出结果是:
(<class '__main__.Parabola_shifted'>, <class '__main__.Geometry'>, <class '__main__.Parabola'>, <class '__main__.Geometry_0'>, <class 'object'>)
一步步拆解Parabola_shifted.x2y的执行流程
当你调用Parabola_shifted.x2y(...)时,执行步骤如下:
Parabola_shifted没有自己定义x2y方法,所以按照MRO列表找到第一个实现该方法的类——Geometry,执行Geometry.x2y,打印in Geometry。- 调用
cls.transform,这里的cls是Parabola_shifted,所以直接执行它自己的transform方法,计算出x_parent = 2 - 2 = 0。 - 关键的一步:执行
super().x2y(...)。这里的super()是相对于Geometry在MRO列表中的位置来查找下一个类——MRO里Geometry的下一个元素是Parabola,所以直接调用Parabola.x2y,打印in Parabola,计算出1 * 0**2 = 0并返回。
为什么MRO是这个顺序?
Python采用C3线性化算法生成MRO,核心原则是:
- 优先保留子类的继承顺序(你定义
Parabola_shifted(Geometry, Parabola)时,先写Geometry再写Parabola,所以MRO会优先遍历Geometry分支) - 保证每个类只会被调用一次
- 维持继承的单调性(不会出现父类在子类之后的情况)
在你的菱形继承结构中(Geometry_0是顶层父类,Geometry和Parabola是中间子类,Parabola_shifted是最终子类),MRO确保了Parabola的方法会在Geometry_0之前被调用,这也是你看到super()指向Parabola的根本原因。
如果把Parabola的x2y方法注释掉,那么super().x2y就会按照MRO继续找到Geometry_0,执行它的x2y方法。
内容的提问来源于stack exchange,提问作者Márton Markó
相关产品推荐
相关产品推荐

