PEP 548 – 更灵活的循环控制
- 作者:
- R David Murray
- 状态:
- 已拒绝
- 类型:
- 标准跟踪
- 创建日期:
- 2017年9月5日
- Python 版本:
- 3.7
- 发布历史:
- 2017 年 8 月 5 日
拒绝说明
Guido 的拒绝:https://mail.python.org/pipermail/python-dev/2017-September/149232.html
摘要
此 PEP 提议增强 break 和 continue 语句,增加一个可选的布尔表达式,用于控制它们是否执行。这允许更清晰、更简洁地表达循环中的控制流。
动机
引自被拒绝的PEP 315
在每次评估 while 循环条件之前,通常需要执行一些代码。这部分代码经常被复制到循环外部,作为进入循环前执行的设置代码。<setup code> while <condition>: <loop body> <setup code>
该 PEP 被拒绝是因为没有找到比以下形式更好的语法。
while True:
<setup code>
if not <condition>:
break
<loop body>
此 PEP 提出了一种更好的形式,它也适用于 for 循环。它之所以更好,是因为它使循环中的控制流更加明确,同时保持了 Python 的缩进美感。
语法
break 和 continue 语句的语法扩展如下:
break_stmt : "break" ["if" expression]
continue_stmt : "continue" ["if" expression]
此外,while 语句的语法修改如下:
while_stmt : while1_stmt|while2_stmt
while1_stmt : "while" expression ":" suite
["else" ":" suite]
while2_stmt : "while" ":" suite
语义
break if 或 continue if 的执行仅当 expression 计算为 true 时发生。
没有表达式的 while 语句会一直循环,直到执行 break 或 return(或引发错误),就如同 while True 语句一样。鉴于循环除了会中断 else 子句执行的方式之外,永远无法终止,因此在没有表达式的形式中不允许使用 else 子句。如果可能,如果一个没有表达式的 while 循环体不包含至少一个 break 或 return 语句,也应视为错误。
理由和示例
之前“最佳可能”的形式
while True:
<setup code>
if not <condition>:
break
<loop body>
可以格式化为
while True:
<setup code>
if not <condition>: break
<loop body>
这在表面上几乎与此 PEP 提出的形式相同。
while:
<setup code>
break if not <condition>
<loop body>
这里显着的区别在于,循环流控制关键字出现在代码行的前面。这使得一眼就能更容易地理解循环中的控制流,尤其是在阅读彩色代码时。
例如,这是一个常见的代码模式,此例取自 tarfile 模块
while True:
buf = self._read(self.bufsize)
if not buf:
break
t.append(buf)
阅读这段代码,我们要么看到 break,然后可能需要思考它适用于哪个 while,因为 break 在 if 下缩进,然后反向追踪阅读触发它的条件;要么,我们先阅读条件,然后才发现这个条件会改变循环的流。
使用新语法,这将变成
while:
buf = self._read(self.bufsize)
break if not buf
t.append(buf)
阅读这段代码,我们首先看到 break,它显然适用于 while,因为它与循环体的缩进级别相同,然后我们阅读导致控制流改变的条件。
此外,考虑一个更复杂的例子,来自 sre_parse
while True:
c = self.next
self.__next()
if c is None:
if not result:
raise self.error("missing group name")
raise self.error("missing %s, unterminated name" % terminator,
len(result))
if c == terminator:
if not result:
raise self.error("missing group name", 1)
break
result += c
return result
这是根据当前 Python 循环控制语法编写此代码的自然方式。然而,有了 break if,更自然的写法如下:
while:
c = self.next
self.__next()
break if c is None or c == terminator
result += c
if not result:
raise self.error("missing group name")
elif c is None:
raise self.error("missing %s, unterminated name" % terminator,
len(result))
return result
这种形式将错误处理移出了循环体,使得循环逻辑更加易于理解。虽然使用当前语法写成这样是完全可能的,但提出的语法使其更自然地以更清晰的形式编写。
提出的语法还为其他语言中常见的经典 repeat ... until <expression> 结构提供了一种自然、Pythonic 的拼写方式,而以前这种结构在 Python 中一直没有找到好的语法。
while:
...
break if <expression>
例如,tarfile 模块中有几个“读取直到”循环,如下所示:
while True:
s = self.__read(1)
if not s or s == NUL:
break
使用新语法,这将读起来更清晰:
while:
s = self.__read(1)
break if not s or s == NUL
将此语法扩展到 continue 的理由不那么充分,但得到了一致性的价值支持。
在多行 if 语句的末尾出现 continue 语句的情况要常见得多,例如,来自 zipfile 的这个例子:
while True:
try:
self.fp = io.open(file, filemode)
except OSError:
if filemode in modeDict:
filemode = modeDict[filemode]
continue
raise
break
对于这个循环,新语法唯一能提供的改进就是省略 True 令牌。
另一方面,考虑来自 uuid.py 的这个例子:
for i in range(adapters.length):
ncb.Reset()
ncb.Command = netbios.NCBRESET
ncb.Lana_num = ord(adapters.lana[i])
if win32wnet.Netbios(ncb) != 0:
continue
ncb.Reset()
ncb.Command = netbios.NCBASTAT
ncb.Lana_num = ord(adapters.lana[i])
ncb.Callname = '*'.ljust(16)
ncb.Buffer = status = netbios.ADAPTER_STATUS()
if win32wnet.Netbios(ncb) != 0:
continue
status._unpack()
bytes = status.adapter_address[:6]
if len(bytes) != 6:
continue
return int.from_bytes(bytes, 'big')
这变成了:
for i in range(adapters.length):
ncb.Reset()
ncb.Command = netbios.NCBRESET
ncb.Lana_num = ord(adapters.lana[i])
continue if win32wnet.Netbios(ncb) != 0
ncb.Reset()
ncb.Command = netbios.NCBASTAT
ncb.Lana_num = ord(adapters.lana[i])
ncb.Callname = '*'.ljust(16)
ncb.Buffer = status = netbios.ADAPTER_STATUS()
continue if win32wnet.Netbios(ncb) != 0
status._unpack()
bytes = status.adapter_address[:6]
continue if len(bytes) != 6
return int.from_bytes(bytes, 'big')
这个例子表明,确实存在非小众的使用场景,其中 continue if 也能提高循环代码的可读性。
值得注意的是,此 PEP 中选择的所有示例都是通过搜索标准库中的 while True 和 continue 找到的,而相关的示例是在检查的前四个模块中找到的。
版权
本文档已置于公共领域。
来源:https://github.com/python/peps/blob/main/peps/pep-0548.rst