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"
应该返回什么,意见不一……
备选方案
致谢
我感谢 Huaiyu Zhu 发起此讨论,并感谢 James Rawlings 以及各个 Python 课程的学生就数值程序员真正关心的问题进行的讨论。
参考文献
来源:https://github.com/python/peps/blob/main/peps/pep-0211.rst