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