Following system colour scheme - Python 增强提案 Selected dark colour scheme - Python 增强提案 Selected light colour scheme - Python 增强提案

Python 增强提案

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 提议增强 breakcontinue 语句,增加一个可选的布尔表达式,用于控制它们是否执行。这允许更清晰、更简洁地表达循环中的控制流。

动机

引自被拒绝的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 ifcontinue if 的执行仅当 expression 计算为 true 时发生。

没有表达式的 while 语句会一直循环,直到执行 break 或 return(或引发错误),就如同 while True 语句一样。鉴于循环除了会中断 else 子句执行的方式之外,永远无法终止,因此在没有表达式的形式中不允许使用 else 子句。如果可能,如果一个没有表达式的 while 循环体不包含至少一个 breakreturn 语句,也应视为错误。

理由和示例

之前“最佳可能”的形式

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 Truecontinue 找到的,而相关的示例是在检查的前四个模块中找到的。


来源:https://github.com/python/peps/blob/main/peps/pep-0548.rst

最后修改:2025-02-01 08:59:27 GMT