PEP 284 – 整数 for-循环
- 作者:
- David Eppstein <eppstein at ics.uci.edu>, Gregory Ewing <greg.ewing at canterbury.ac.nz>
- 状态:
- 已拒绝
- 类型:
- 标准跟踪
- 创建日期:
- 2002年3月1日
- Python 版本:
- 2.3
- 发布历史:
摘要
本PEP建议通过扩展“for”关键字后允许的表达式范围,以允许三向比较(例如),来简化整数区间的迭代:
for lower <= var < upper:
代替当前的
for item in list:
语法。由此产生的循环或列表迭代将遍历使比较结果为真的所有变量值,从给定区间的左端点开始。
声明
本PEP被拒绝。该提案存在一些可修复的问题(参见 Raymond Hettinger 于2005年6月18日在 python-dev 邮件列表中的帖子中列出的修复措施[1])。然而,即使进行了修复,该提案也未能获得支持。具体来说,Guido 不认为 range()
格式需要修复:“range()
的全部意义(15年前)是为了避免需要语法来指定数字循环。我认为它运作良好,没有什么需要修复的(除了 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-loop 语法左侧和右侧出现的两个 or_expr 称为循环的界限,将中间的 or_expr 称为循环的变量。当使用新语法的 for-loop 执行时,两个界限的表达式都将被求值,并创建一个迭代器对象,根据所使用的比较操作,迭代遍历两个界限之间的所有整数。迭代器将从一个等于或接近左界限的整数开始,然后以 +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
最后修改: 2025-02-01 08:55:40 GMT