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

Python 增强提案

PEP 601 – 禁止在 finally 块中使用 return/break/continue 跳出

作者:
Damien George, Batuhan Taskaya
发起人:
Alyssa Coghlan
讨论至:
Discourse 帖子
状态:
已拒绝
类型:
标准跟踪
创建日期:
2019年8月26日
Python 版本:
3.8
发布历史:
2019年8月26日, 2019年9月23日
取代者:
765
决议:
Discourse 消息

目录

拒绝说明

此 PEP 已被指导委员会投票否决,比率为 4/4。

Guido 否决此 PEP 的论点是:“在我看来,大多数语言都实现了这种构造,但有风格指南和/或 Linter 拒绝它。我支持将此添加到 PEP 8 的提案”,以及“我注意到玩具示例有些误导——可能有用的功能是在 finally 块中的条件 return(或 break 等)。”。

摘要

此 PEP 提议禁止在 finally 块中使用会跳出 finallyreturnbreakcontinue 语句。在 such a location 使用它们会 silently 取消通过 finally 传播的任何活动异常,导致代码不清晰且可能出现错误。

在 Python 3.7 中,continue 当前在 finally 中不受支持(由于实现问题),而此提案是不在 Python 3.8 中添加对其的支持。对于 returnbreak,提案是在 Python 3.9 中弃用其使用,在 Python 3.10 中发出编译警告,然后禁止其使用。

动机

finally 块中 returnbreakcontinue 的使用会导致行为不明显。考虑以下函数

def foo():
    try:
        foo()
    finally:
        return

即使它有无限递归并在 try 中引发异常,此函数也会干净地返回(不带异常)。原因是 finally 中的 return 会 silently 取消任何通过 finally 块传播的异常。这种行为是出乎意料且不明显的。此函数等同于

def foo():
    try:
        foo()
    except:
        pass
    return

如果 breakcontinue 跳到 finally 块外部的代码,它们会产生类似的行为(它们会 silently 抑制异常)。例如

def bar():
    while True:
        try:
            1 / 0
        finally:
            break

这种行为违背了《Python 之禅》的以下部分:

  • 显式优于隐式——异常被隐式地抑制
  • 可读性很重要——代码的意图不明显
  • 错误永远不应悄悄地发生;除非明确抑制——异常被隐式地抑制

如果确实需要这种抑制异常的行为,那么可以使用显式的 try-except 形式,这使得代码更清晰。

与语义无关,在 finally 块中实现 return/break/continue 并非易事,因为它需要在运行时正确跟踪任何活动的异常(正在执行的 finally 块可能有也可能没有活动异常)并适当地取消它们。CPython 在 continue 的情况下存在一个 bug,因此最初不允许它 [1]。要求在 finally 中正确处理 return/break/continue 会给 Python 的其他实现带来不必要的负担。

其他语言

Java 允许从 finally 块返回,但根据 [2][3][4],不鼓励使用。Java 编译器后来包含了 -Xlint:finally 这一 linting 选项来警告在 finally 块中使用 return。Eclipse 编辑器也对此用法发出警告。

Ruby 允许从 ensure(Python 的 finally)内部返回,但它应该是显式的 return。这是不被鼓励的,并且由 Linter 处理 [5][6]

与 Ruby 类似,JavaScript 也允许在 finally 块中使用 return/break/continue,但这被视为不安全,并且由 eslint 处理 [7]

C# 禁止在 finally 块中使用结束语句,如 return/goto/break [8][9]

基本原理

由于 return/break/continuefinally 块中的行为不明确,并且该模式很少使用,而且编写等效代码(更显式)有简单的替代方案,因此禁止该语法是最直接的方法。

规范

这是对编译器的更改,而不是对语法的更改。编译器应检查 finally 块中的以下内容:

  • 在任何嵌套级别的任何语句中使用 return
  • 在任何嵌套级别的任何语句中使用 break/continue,它会将控制流转移到 finally 块外部。

一旦发现这种情况,它应发出适当的异常:

  • 对于 continueSyntaxError(这是 3.7 的当前行为)。
  • 对于 return/break,3.10 中为 SyntaxWarning,之后为 SyntaxError

例如,以下所有内容都将被此提案禁止:

def f():
    try:
        pass
    finally:
        return

def g():
    try:
        pass
    finally:
        try:
            return
        finally:
            pass

def h():
    try:
        pass
    finally:
        try:
            pass
        finally:
            for x in range(10):
                return

以下情况仍然允许,因为 continue 没有跳出 finally 块:

try:
    pass
finally:
    for x in range(10):
        continue

请注意,此 PEP 仍然允许从 finally 块中 yield,因为恢复生成器将恢复 finally 并最终引发任何活动异常(因此它们永远不会被 yield 抑制)。

向后兼容性

这对于 returnbreak 来说是一个不兼容的向后更改。

在 CPython 标准库(v3.8.0b1-651-g7fcc2088a5)中,以下位置在 finally 块中使用 return

  • Lib/subprocess.py:921 - 这里的用法看起来像一个 bug
  • Lib/multiprocessing/connection.py:316 - 这里的用法看起来合法,但意图不明确
  • Lib/multiprocessing/connection.py:318 - 这里的用法看起来合法,但意图不明确
  • Lib/test/test_sys_settrace.py:837 - 一个测试 returnfinally 块中的用法
  • Lib/test/test_sys_settrace.py:1346 - 一个测试 returnfinally 块中的用法

在标准库中,没有在 finally 块中使用 break(会跳出 finally 块)的用法。

安全隐患

这是对语言的简化,以及相关代码的移除,因此不应引入新的安全漏洞。

如何教授此内容

此功能很少使用,因此禁止它很可能只会影响高级用户,而不是初学者,也不会影响现有的教学材料。由于这是对一个功能的移除,当用户使用被禁止的功能时,会引发 SyntaxError,这将是用户学习的方式。

参考实现

目前没有参考实现,尽管 continue 在 finally 中当前的处理方式(引发 SyntaxError)可以扩展到 returnbreak

参考资料


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

最后修改:2025-02-01 08:55:40 GMT