Python total_ordering:为什么要使用__lt__和__eq__而不是__le__?

22
在Python3中,functools.total_ordering装饰器允许我们只重载__lt____eq__来获得所有6个比较运算符。我不明白为什么要写两个运算符,如果只写一个的话,即__le__或者__ge__,其他的运算符就会相应地被定义。
a < b   <=>   not (b <= a)
a > b   <=>   not (a <= b)
a == b   <=>   (a <= b) and (b <= a)
a != b   <=>   (a <= b) xor (b <= a)

那是因为异或运算符在本地不存在吗?


3
"XOR" 存在于计算机原生语言中。 - Martijn Pieters
1
@MartijnPieters 只是按位异或,不是吗? - Guillaume Lemaître
1
按照所有意图,位异或运算符在Python和许多其他编程语言中都是布尔异或运算符。在Python中,boolint的子类,False == 0True == 1。因此,True ^ False等运算可以正常工作。 - mtraceur
1个回答

32
文档说明您必须定义__lt__()__le__()__gt__()__ge__()中的一个,但只应提供一个__eq__()方法。换句话说,__eq__()方法是可选的。total_ordering实现不要求您指定__eq__()方法;它仅测试__lt__()__le__()__gt__()__ge__()方法。它会基于这4个方法之一提供最多3个缺失的特殊方法。

您不能仅基于__le____ge__来排序,因为您不能假设可以交换ab;如果b是不同的类型,则可能未实现b.__le__,因此无法保证您的a < b <=> not (b <= a)映射。如果未定义__le__但已定义__lt__,则实现使用(a <= b) and (a != b)

完整的映射表如下:

比较 可用 替代方案
a > b a < b (not a < b) and (a != b)
a <= b (not a <= b)
a >= b (a >= b) and (a != b)
a <= b a < b (a < b) or (a == b)
a > b (not a > b)
a >= b (not a >= b) or (a == b)
a < b a <= b (a <= b) and (a != b)
a > b (not a > b) and (a != b)
a >= b (not a >= b)
a >= b a < b (not a < b)
a <= b (not a <= b) or (a == b)
a > b (a > b) or (a == b)
"

__eq__方法是可选的,因为基本的object对象已经为您定义了一个; 仅当两个实例是相同的对象时才被视为相等; ob1 == ob2只有当ob1 is ob2True时才成立。请参见object.c中的do_richcompare()函数; 请记住,那里的==运算符正在比较指针。

"

有没有一种方法可以指示total_ordering(或另一种方法),使得您只想让比较在相同类型的对象之间起作用,这样您就可以推断出a < b意味着b < a也可用,然后从仅提供一个操作符实现中推断出所有6个比较运算符? - dylanmorroll
@dylanmorroll:仔细阅读我的答案,它已经解释了它已经实现了你想要的功能:total_ordering实现[...]测试__lt __()__le __()__gt __()__ge __()方法。它基于这4个方法之一提供最多3个缺失的特殊方法。顺便说一下,只有5种比较方法,而不是6种。 - Martijn Pieters
@dylanmorroll:如果你的比较涉及到一个不支持的不同类型,你可以从你的__lt__实现中返回NotImplemented,而total_ordering会处理正确的事情。 - Martijn Pieters
也许我应该澄清一下,我的意思是获取所有比较运算符,包括适当的相等性(而不是隐式相等性,它是一个身份检查)。啊,是的,我在我的6个计数中包括不等于,但我想否定不是一个新的运算符。 - dylanmorroll
@dylanmorroll 然后提供一个 __eq__ 实现并利用 NotImplemented 信号对象。 - Martijn Pieters

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接