python - 为什么 Python 不提供 __le__ 和 __ge__ 的默认实现?
问题描述
以下比较关系(=、≠、<、>、≤ 和 ≥)之间的数学关系始终有效,因此在 Python 中默认实现(除了 2 个联合关系,这似乎是任意的,这也是本文的原因):
- 2互补关系:“=和≠互为互补”;
- 6个逆向关系*:“=是自身的逆向关系”、“≠是自身的逆向关系”、“<和>是彼此的逆向关系”、“≤和≥是彼此的逆向关系”;
- 2个并集关系:“≤是<和=的并集,”≥是>和=的并集。
以下比较关系之间的关系仅对总订单functools.total_ordering
有效,因此在 Python 中默认不实现(但用户可以通过 Python 标准库提供的类装饰器方便地实现它们):
- 4种互补关系:“<和≥互为补”,">和≤互为补”。
为什么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 中实现。
解决方案
为什么做出这个决定只有原作者才知道,但根据手册中的这些提示,可以推断出原因:
要从单个根操作自动生成排序操作,请参阅
functools.total_ordering()
。
虽然这个装饰器可以很容易地创建表现良好的完全有序类型,但它确实以派生比较方法的执行速度较慢和更复杂的堆栈跟踪为代价。如果性能基准测试表明这是给定应用程序的瓶颈,则实施所有六种丰富的比较方法可能会提供简单的速度提升。
将此与 Python 的“显式优于隐式”的口头禅相结合,以下推理应该是令人满意的:
派生实际上是免费的,它只是操作__ne__
,即反转布尔值。__eq__
not o.__eq__(other)
__le__
但是,从 和 的并集__lt__
派生意味着__eq__
需要调用这两种方法,如果完成的比较足够复杂,尤其是与优化的单个__le__
实现相比,这可能会对性能造成很大影响。Python 允许您通过使用total_ordering
装饰器显式地选择这种便利而不是性能,但它不会隐式地强加给您。
如果您尝试进行未实现的比较而不是隐式派生比较,您也可能会争论显式错误,而隐式派生比较可能会产生细微的错误,具体取决于您对自定义类的用途。Python 不会在这里为您做任何猜测,而是让您自己明确地实现您想要的比较,或者再次明确地选择加入派生比较。
推荐阅读
- javascript - 即使对的顺序不同但相同的数字对也对数字进行分组和求和
- c# - 带有 OpenId (AuthenticationTypes.Federation) 的 SignalR Hub 返回 401
- laravel - Laravel 项目不起作用 - 内部服务器错误
- azure - 无法通过 LogicApps 更新 CosmosDB
- python - 将字符串附加到修改的行
- tosca - 加快“如果”条件(验证/等待)
- javascript - 不变违规:requireNativeComponent:“BVLinearGradient”
- javascript - Mailgun 帐户在提交到 GitHub 后停用
- string - 如何键入包含少于 5 个字符的字符串并仍然继续下一行代码?
- bash - watch 命令不适用于特殊字符和引号