PEP 535 – 丰富比较链
- 作者:
- Alyssa Coghlan <ncoghlan at gmail.com>
- 状态:
- 延期
- 类型:
- 标准跟踪
- 需要:
- 532
- 创建:
- 2016-11-12
- Python 版本:
- 3.8
PEP 延期
对该 PEP 的进一步考虑已推迟到 Python 3.8 或更高版本。
摘要
受 PEP 335 的启发,并以 PEP 532 中描述的断路协议为基础,本 PEP 提出更改比较链的定义,其中比较链将被更新为使用左结合断路运算符(else
)而不是逻辑析取运算符(and
),如果左边的比较结果返回断路器。
虽然 NumPy 中当前对单值数组的处理有一些实际上的复杂性,但此更改足以允许矩阵的逐元素比较链操作,结果为一个布尔值矩阵,而不是引发 ValueError
或 tautologically 返回 True
(表示非空矩阵)。
与其他 PEP 的关系
本 PEP 已从 PEP 532 的早期版本中提取出来,作为断路协议的后续用例,而不是其引入的必要部分。
本 PEP 中处理逐元素比较用例的具体提案,是通过更改比较链的语义定义来直接从 Guido 对 PEP 335 的拒绝中得出的。
规范
一个像 0 < x < 10
这样的比较链写成
LEFT_BOUND LEFT_OP EXPR RIGHT_OP RIGHT_BOUND
目前在语义上大致等同于
_expr = EXPR
_lhs_result = LEFT_BOUND LEFT_OP _expr
_expr_result = _lhs_result and (_expr RIGHT_OP RIGHT_BOUND)
使用 PEP 532 中引入的断路概念,本 PEP 提出更改比较链,以明确检查左边的比较是否返回断路器,如果是,则使用 else
而不是 and
来实现比较链
_expr = EXPR
_lhs_result = LEFT_BOUND LEFT_OP _expr
if hasattr(type(_lhs_result), "__else__"):
_expr_result = _lhs_result else (_expr RIGHT_OP RIGHT_BOUND)
else:
_expr_result = _lhs_result and (_expr RIGHT_OP RIGHT_BOUND)
这允许像 NumPy 数组这样的类型通过从比较操作返回适当定义的断路器来控制比较链的行为。
此逻辑扩展到任意数量的比较链操作,与 and
的现有扩展相同。
基本原理
在最终拒绝 PEP 335 时,Guido van Rossum 指出 [1]
NumPy 团队提出了一个稍微不同的问题:对他们来说,最常见的用例是比较链(例如 A < B < C)。
要理解这个观察结果,我们首先需要看看比较是如何与 NumPy 数组一起工作的
>>> import numpy as np
>>> increasing = np.arange(5)
>>> increasing
array([0, 1, 2, 3, 4])
>>> decreasing = np.arange(4, -1, -1)
>>> decreasing
array([4, 3, 2, 1, 0])
>>> increasing < decreasing
array([ True, True, False, False, False], dtype=bool)
这里我们看到 NumPy 数组比较默认情况下是逐元素的,将左边的数组中的每个元素与右边的数组中的对应元素进行比较,并生成一个布尔值矩阵。
如果比较的两边都是标量值,则它将被广播到整个数组中,并与每个单独的元素进行比较
>>> 0 < increasing
array([False, True, True, True, True], dtype=bool)
>>> increasing < 4
array([ True, True, True, True, False], dtype=bool)
但是,如果我们尝试使用比较链,这个广播习惯用法就会失效
>>> 0 < increasing < 4
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ValueError: The truth value of an array with more than one element is ambiguous. Use a.any() or a.all()
问题是,在内部,Python 隐式地将此比较链扩展为以下形式
>>> 0 < increasing and increasing < 4
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ValueError: The truth value of an array with more than one element is ambiguous. Use a.any() or a.all()
而 NumPy 仅允许对单元素数组隐式强制转换为布尔值,其中 a.any()
和 a.all()
可以保证具有相同的结果
>>> np.array([False]) and np.array([False])
array([False], dtype=bool)
>>> np.array([False]) and np.array([True])
array([False], dtype=bool)
>>> np.array([True]) and np.array([False])
array([False], dtype=bool)
>>> np.array([True]) and np.array([True])
array([ True], dtype=bool)
本 PEP 中的提案将允许通过更新 NumPy 中逐元素比较操作的定义来更改这种情况,以返回一个专门的子类,该子类实现了新的断路协议,并更改结果数组在布尔上下文中的解释,始终返回 False
,因此永远不会触发短路行为
class ComparisonResultArray(np.ndarray):
def __bool__(self):
# Element-wise comparison chaining never short-circuits
return False
def _raise_NotImplementedError(self):
msg = ("Comparison array truth values are ambiguous outside "
"chained comparisons. Use a.any() or a.all()")
raise NotImplementedError(msg)
def __not__(self):
self._raise_NotImplementedError()
def __then__(self, result):
self._raise_NotImplementedError()
def __else__(self, result):
return np.logical_and(self, other.view(ComparisonResultArray))
有了这个更改,上面的比较链示例将能够返回
>>> 0 < increasing < 4
ComparisonResultArray([ False, True, True, True, False], dtype=bool)
实施
实际实施已被推迟,以等待对在 PEP 532 中提出的更改进行原则上的兴趣。
…待定…
参考
版权
本文件已根据 CC0 1.0 许可证的条款置于公有领域:https://creativecommons.org/publicdomain/zero/1.0/
来源:https://github.com/python/peps/blob/main/peps/pep-0535.rst
最后修改时间:2023-10-11 12:05:51 GMT