PEP 211 – 添加一个新的外积运算符
- 作者:
- Greg Wilson <gvwilson at ddj.com>
- 状态:
- 已拒绝
- 类型:
- 标准跟踪
- 创建日期:
- 2000年7月15日
- Python 版本:
- 2.1
- 发布历史:
引言
本 PEP 描述了一项提案,旨在将 @(发音为“across”)定义为 Python 2.2 中的一个新的外积运算符。当应用于序列(或其他可迭代对象)时,此运算符将组合它们的迭代器,因此
for (i, j) in S @ T:
pass
将等价于
for i in S:
for j in T:
pass
类将能够使用特殊方法 __across__、__racross__ 和 __iacross__ 重载此运算符。特别是,新的 Numeric 模块(PEP 209)将为多维数组重载此运算符以实现矩阵乘法。
背景
数值计算现在只是计算的一小部分,但许多程序员——包括许多 Python 用户——仍然需要用代码表达复杂的数学运算。大多数数值语言,如 APL、Fortran-90、MATLAB、IDL 和 Mathematica,因此提供了两种形式的常见算术运算符。一种形式逐元素工作,例如将矩阵参数的对应元素相乘。另一种实现了该操作的“数学”定义,例如执行行-列矩阵乘法。
Zhu 和 Lielens 提出以这种方式使 Python 的运算符加倍。他们的提案将创建六个新的二元中缀运算符和六个新的原地运算符。
此提案的最初版本更为保守。作者咨询了 GNU Octave [1](MATLAB 的开源克隆)的开发者。其开发者同意提供一个用于矩阵乘法的中缀运算符很重要:数值程序员确实关心他们是必须写 mmul(A,B) 而不是 A op B。
另一方面,当被问及拥有用于矩阵求解和其他操作的中缀运算符有多重要时,James Rawlings 教授回答说 [2]
我**不**认为这是必须的,我做了很多矩阵求逆。我不记得是A\b还是b\A,所以我总是写inv(A)*b。我建议放弃\。
基于这次讨论,以及来自美国国家实验室和其他地方的学生的反馈,我们建议只向 Python 添加一个新的运算符,用于矩阵乘法。
迭代器
Python 2.2 计划添加迭代器,这为本提案开辟了更广阔的范围。作为 PEP 201(同步迭代)讨论的一部分,本提案的作者进行了一项非正式可用性实验 [3]。结果表明,用户在心理上接受“交叉积”循环语法。例如,大多数用户预期
S = [10, 20, 30]
T = [1, 2, 3]
for x in S; y in T:
print x+y,
会打印 11 12 13 21 22 23 31 32 33。我们相信用户对
for (x, y) in S @ T:
print x+y
也会有同样的反应,即他们会自然地将其解释为编写嵌套循环的简洁方式。
这就是迭代器发挥作用的地方。在执行循环之前实际构建两个(或更多)序列的交叉积会非常昂贵。另一方面,@ 可以定义为获取其参数的迭代器,然后创建一个外部迭代器,该迭代器返回内部迭代器返回的值的元组。
讨论
- 添加一个名为“across”的函数对 Python 的影响将小于一个新的中缀运算符。然而,这不会让 Python 对数值程序员更具吸引力,他们确实关心他们是否可以使用运算符编写矩阵乘法,或者是否必须将其编写为函数调用。
@应该以与比较运算符相同的方式可链式使用,即(1, 2) @ (3, 4) @ (5, 6)
必须返回
(1, 3, 5) ... (2, 4, 6),而**不是**((1, 3), 5) ... ((2, 4), 6)。这不应该需要解析器的特殊支持,因为第一个@创建的外部迭代器可以很容易地被教导如何与普通迭代器组合。- 必须有某种方法来区分可重新启动的迭代器和不可重新启动的迭代器。例如,如果
S是输入流(例如文件),L是列表,那么S @ L是直接的,但L @ S则不然,因为流的迭代不能重复。这可以被视为错误,或者通过让外部迭代器检测不可重新启动的内部迭代器并缓存它们的值来处理。 - 在三位 Python 初学者(都是经验丰富的程序员)面前对该提案进行白板测试表明,用户将期望
"ab" @ "cd"
返回四个字符串,而不是四个字符对的元组。对于
("a", "b") @ "cd"
应该返回什么,意见不一……
备选方案
致谢
我感谢朱怀宇发起这次讨论,并感谢 James Rawlings 和各种 Python 课程的学生就数值程序员真正关心的问题进行的讨论。
参考资料
来源:https://github.com/python/peps/blob/main/peps/pep-0211.rst