Following system colour scheme Selected dark colour scheme Selected light colour scheme

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

语义

仅当 expression 求值为真时,才会执行 break ifcontinue if

没有表达式的 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> 结构的自然、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 Truecontinue 找到的,并且相关的示例是在检查的前四个模块中找到的。


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

上次修改: 2023年9月9日 17:39:29 GMT