PEP 284 - 整数 for 循环
- 作者:
- David Eppstein <eppstein at ics.uci.edu>, Gregory Ewing <greg.ewing at canterbury.ac.nz>
- 状态:
- 已拒绝
- 类型:
- 标准跟踪
- 创建时间:
- 2002-03-01
- Python 版本:
- 2.3
- 历史记录:
摘要
本 PEP 提出通过扩展“for”关键字后允许的表达式范围,允许三元比较,例如
for lower <= var < upper:
代替当前的
for item in list:
语法,来简化对整数区间的迭代。生成的循环或列表迭代将遍历使比较结果为真的所有 var 值,从给定区间的左端点开始。
宣告
本 PEP 被拒绝。该提案存在许多可以解决的问题(请参阅 Raymond Hettinger 于 2005 年 6 月 18 日在 python-dev 上发布的帖子中列出的修复方案[1])。然而,即使解决了这些问题,该提案也未能获得支持。具体来说,Guido 不认可需要修复 range()
格式的理由,“15 年前 range()
的意义在于避免需要语法来指定对数字的循环。我认为它运行良好,没有需要修复的地方(除了 range()
需要成为一个迭代器,这将在 Python 3.0 中实现)。”
基本原理
Python 中最常见的 for 循环用途之一是对整数区间进行迭代。Python 提供了 range()
和 xrange()
函数来为这些区间生成列表和迭代器,这些函数最适合最常见的情况:从零开始的半开区间。但是,range()
语法对于开区间或闭区间来说更笨拙,并且在反转迭代顺序时缺乏对称性。此外,对不熟悉函数的调用使得 Python 新手难以理解使用 range()
或 xrange()
的代码。
人们普遍认为缺乏自然、直观的整数迭代语法,导致了 python-list 上的激烈争论,并在本提案之前产生了至少四个 PEP。 PEP 204(已拒绝)建议重用 Python 的切片语法来表示整数区间,这将导致更简洁的语法,但无法解决多参数 range()
的可读性问题。 PEP 212(已延期)提出了几种语法来直接将列表转换为整数索引序列,以取代当前的习惯用法
range(len(list))
用于这种转换,而 PEP 281 提出通过允许将其写成以下形式来简化相同的习惯用法
range(list).
PEP 276 提出允许将整数自动转换为迭代器,从而简化最常见的半开情况,但没有解决其他类型区间的复杂性。python-list 上还讨论了其他替代方案。
此处描述的解决方案是在“for”关键字后允许三元比较,无论是在 for 循环的上下文中还是在列表推导的上下文中
for lower <= var < upper:
这将导致对连续整数区间进行迭代,从比较中的左边界开始,到右边界结束。使用的确切比较运算将决定区间在任一端的开闭性以及整数是按升序还是降序考虑。
此语法与标准数学符号非常接近,因此 Python 新手可能比目前的 range()
语法更熟悉。开区间和闭区间端点同样易于表达,整数区间的反转可以通过简单地交换两个端点并反转比较来实现。此外,此类循环的语义与现有 Python for 循环的一种解释方式非常相似
for item in list
精确地遍历使表达式
item in list
为真的所有 item 值。同样,新格式
for lower <= var < upper:
将精确地遍历使表达式
lower <= var < upper
为真的所有整数 var 值。
规范
我们建议扩展 for 语句的语法,目前为
for_stmt: "for" target_list "in" expression_list ":" suite
["else" ":" suite]
如下所述
for_stmt: "for" for_test ":" suite ["else" ":" suite]
for_test: target_list "in" expression_list |
or_expr less_comp or_expr less_comp or_expr |
or_expr greater_comp or_expr greater_comp or_expr
less_comp: "<" | "<="
greater_comp: ">" | ">="
同样,我们建议扩展列表推导的语法,目前为
list_for: "for" expression_list "in" testlist [list_iter]
将其替换为
list_for: "for" for_test [list_iter]
在所有情况下,由 for_test 形成的表达式将遵循与表达式中的比较相同的优先级规则。for_test 中的两个 comp_operators 必须都为相同类型,这与表达式中的链式比较不同,链式比较没有这种限制。
我们将 for 循环语法左右两侧出现的两个 or_expr 分别称为循环的边界,中间的 or_expr 称为循环的变量。当执行使用新语法的 for 循环时,将对两个边界的表达式进行求值,并创建一个迭代器对象,该对象根据所使用的比较运算遍历两个边界之间的所有整数。迭代器将从等于或接近左边界的一个整数开始,然后以 +1 或 -1 的步长遍历剩余的整数,如果比较运算在由 less_comp 或 greater_comp 分别描述的集合中。然后执行将继续进行,就好像表达式为
for variable in iterator
其中“variable”指循环的变量,“iterator”指为给定整数区间创建的迭代器。
整数 for 循环中循环变量的值可以是普通整数或长整数,具体取决于边界的幅度。整数 for 循环的两个边界都必须求值为实数类型(整数、长整数或浮点数)。任何其他值都将导致 for 循环语句引发 TypeError
异常。
问题
以下问题是在 Python 列表中讨论本提案和相关提案时提出的。
- 右边界应该求值一次还是每次循环时都求值?显然,左边界只求值一次是有意义的。出于一致性和效率的考虑,我们为右边界选择了相同的约定。
- 虽然新语法大大简化了整数 for 循环,但使用新语法的列表推导并不那么简单。我们认为这是合理的,因为 for 循环比推导更频繁。
- 该提案不允许访问诸如由
xrange
创建的整数迭代器对象。没错,但我们认为这是通用列表推导语法中的一个缺陷,超出了本提案的范围。此外,xrange()
仍然可用。 - 该提案不允许使用除 1 和 -1 之外的增量。更一般的算术级数需要由
range()
或xrange()
创建,或者由列表推导语法创建,例如[2*x for 0 <= x <= 100]
- 循环变量在三元比较中的位置不像当前的
for item in list
语法中的变量那么明显,这会导致潜在的可读性下降。我们认为,这种损失被自然整数迭代语法带来的可读性提高所抵消。
- 在某种程度上,本 PEP 解决了与 PEP 276 相同的问题。我们认为这两个 PEP 并不冲突,因为 PEP 276 主要关注从 0 开始的半开区间(
range()
的简单情况),而本 PEP 主要关注简化所有其他情况。然而,如果本 PEP 被批准,其用于整数循环的新简化语法可能会在一定程度上减少对 PEP 276 的需求。 - 目前尚不清楚允许使用浮点数边界来表示整数循环是否有意义:如果浮点数表示近似值,它如何用于确定精确的整数序列?另一方面,不允许使用浮点数边界将使在整数 for 循环中使用
floor()
和ceiling()
变得困难,就像现在在使用range()
时很难使用它们一样。我们倾向于灵活,但这可能会导致在确定使给定比较结果为真的最小和最大整数时遇到一些实现困难。 - 是否允许使用 int、long 和 float 以外的类型作为边界?另一种选择是通过
int()
将所有边界转换为整数,并允许任何可以这样转换的东西作为边界,而不是仅仅允许浮点数。但是,这将改变语义:0.3 <= x
与int(0.3) <= x
不同,并且对于以 0.3 作为下边界的循环从零开始将会令人困惑。此外,一般来说,int(f)
可能与f
相差甚远。
实现
目前还没有实现。预计实现不会有任何重大困难:如果需要,可以通过解析每个“for”关键字后的通用表达式,并测试表达式的顶级运算符是否为“in”或三元比较来识别新语法。Python 编译器将把任何新语法的实例转换为对特殊迭代器对象中项目的循环。
参考资料
版权
本文件已进入公有领域。
来源: https://github.com/python/peps/blob/main/peps/pep-0284.rst
最后修改时间: 2023-09-09 17:39:29 GMT