简短的回答是他们希望允许AB 覆盖A 的行为。 Python 无法调用AB.__lt__(a, ab),因为a 对于AB 方法可能不是有效的self,因此它调用了有效的AB.__gt__(ab, a)。
长答案有点复杂。
根据rich comparison operators 的文档:
这些方法没有交换参数版本(当左参数不支持操作但右参数支持时使用);而__lt__()和__gt__()是彼此的反射,__le__()和__ge__()是彼此的反射,__eq__()和__ne__()是自己的反射。
换句话说,x <= y 将调用y.__ge__(x),与x+y 调用y.__radd__(x) 的情况完全相同。比较:
>>> class X(object):
... def __add__(self, other):
... print('X.add')
>>> class Y(object):
... def __radd__(self, other):
... print('Y.radd')
>>> class XY(X, Y):
... pass
>>> x, xy = X(), XY()
>>> x + xy
Y.radd
根据reflected operators 的文档:
调用这些方法来实现二进制算术运算……使用反射(交换)操作数。这些函数只有在左操作数不支持相应操作并且操作数属于不同类型时才会被调用……
注意:如果右操作数的类型是左操作数类型的子类,并且该子类为操作提供了反射方法,则该方法将在左操作数的非反射方法之前调用。这种行为允许子类覆盖其祖先的操作。
所以,因为XY 是X 的子类,所以XY.__radd__ 优先于X.__add__。同样,因为AB 是A 的子类,AB.__ge__ 优先于A.__le__。
这可能应该更好地记录下来。要弄清楚,您必须忽略括号“当左参数不支持操作但右参数支持时使用”,猜测您需要查找正常的交换运算符(没有链接,甚至提到, 这里),然后忽略“仅当左操作数不支持相应操作时才调用这些函数”的措辞,并查看与上面的内容相矛盾的“注释”……还要注意文档明确说,“比较运算符之间没有隐含关系”,只有在描述交换案例之前的一段,这暗示了这种关系……
最后,这种情况看起来很奇怪,因为AB 并没有覆盖__ge__ 本身,而是从B 继承而来,B 对A 一无所知,并且与它无关。大概B 不打算让它的子类覆盖A 的行为。但是,如果 B 打算用作 A 派生类的 mixin,那么它可能会 打算进行这样的覆盖。无论如何,该规则可能已经足够复杂,而无需深入探讨每种方法在 MRO 中的来源。无论是什么原因,__ge__ 的来源都无关紧要;如果它在子类中,就会被调用。
对于您添加的最后一个问题,“我该怎么做才能让 __le__ 调用而不是 __ge__ ??”......好吧,你真的不能,就像你可以调用 X.__add__ 而不是XY.__radd__。当然,您始终可以实现一个调用A.__le__(或X.__add__)的AB.__ge__(或XY.__radd__),但可能更容易实现AB.__ge__,使其与A一起工作首先作为它的另一个论点。或者,您可以删除继承并找到其他方法来建模您以这种方式建模的任何内容。或者您可以显式调用a.__le__(ab) 而不是a<=ab。但除此之外,如果您以一种利用“无隐含关系”的方式设计类来做一些奇怪的事情,那么您就会被文档误导,并且必须以某种方式重新设计它们。