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
语义
仅当 expression
求值为真时,才会执行 break if
或 continue if
。
没有表达式的 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>
结构的自然、Python 风格的拼写,并且之前没有为 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
的理由不那么充分,但得到了保持一致性的价值观的支持。
对于 continue
语句位于多行 if 代码块的末尾的情况更为常见,例如 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
上次修改: 2023年9月9日 17:39:29 GMT