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