首页 > 解决方案 > 为什么 Python 不提供 __le__ 和 __ge__ 的默认实现?

问题描述

以下比较关系(=、≠、<、>、≤ 和 ≥)之间的数学关系始终有效,因此在 Python 中默认实现(除了 2 个联合关系,这似乎是任意的,这也是本文的原因):

以下比较关系之间的关系仅对总订单functools.total_ordering有效,因此在 Python 中默认不实现(但用户可以通过 Python 标准库提供的类装饰器方便地实现它们):

为什么Python只缺少上面的2个联合关系(“≤是联合<和=”,“≥是>和=”的联合)?

它应该提供一个__le__关于and 的默认实现,以及一个关于__lt__and的默认实现,就像这些(但可能在 C 中为了性能,比如):__eq____ge____gt____eq____ne__

def __le__(self, other):
    result_1 = self.__lt__(other)
    result_2 = self.__eq__(other)
    if result_1 is not NotImplemented and result_2 is not NotImplemented:
        return result_1 or result_2
    return NotImplemented

def __ge__(self, other):
    result_1 = self.__gt__(other)
    result_2 = self.__eq__(other)
    if result_1 is not NotImplemented and result_2 is not NotImplemented:
        return result_1 or result_2
    return NotImplemented

2 个联合关系始终有效,因此这些默认实现将使用户不​​必一直提供它们(如这里)。

这是Python 文档的段落,其中明确指出默认情况下当前未实现 2 个联合关系(粗体强调我的):

默认情况下,__ne__()委托__eq__()并反转结果,除非它是NotImplemented. 比较运算符之间没有其他隐含关系,例如,真值(x<y or x==y)不隐含x<=y


* 逆向关系通过NotImplemented协议在 Python 中实现。

标签: pythonrelationshipcomparison-operatorspartial-ordering

解决方案


为什么做出这个决定只有原作者才知道,但根据手册中的这些提示,可以推断出原因:

要从单个根操作自动生成排序操作,请参阅functools.total_ordering()

虽然这个装饰器可以很容易地创建表现良好的完全有序类型,但它确实以派生比较方法的执行速度较慢和更复杂的堆栈跟踪为代价。如果性能基准测试表明这是给定应用程序的瓶颈,则实施所有六种丰富的比较方法可能会提供简单的速度提升。

将此与 Python 的“显式优于隐式”的口头禅相结合,以下推理应该是令人满意的:

派生实际上是免费的,它只是操作__ne__,即反转布尔值。__eq__not o.__eq__(other)

__le__但是,从 和 的并集__lt__派生意味着__eq__需要调用这两种方法,如果完成的比较足够复杂,尤其是与优化的单个__le__实现相比,这可能会对性能造成很大影响。Python 允许您通过使用total_ordering装饰器显式地选择这种便利而不是性能,但它不会隐式地强加给您。

如果您尝试进行未实现的比较而不是隐式派生比较,您也可能会争论显式错误,而隐式派生比较可能会产生细微的错误,具体取决于您自定义类的用途。Python 不会在这里为您做任何猜测,而是让您自己明确地实现您想要的比较,或者再次明确地选择加入派生比较。


推荐阅读