PEP 760 – 禁止裸露的 except
- 作者:
- Pablo Galindo <pablogsal at python.org>, Brett Cannon <brett at python.org>
- 状态:
- 已撤回
- 类型:
- 标准跟踪
- 创建日期:
- 2024年10月2日
- Python 版本:
- 3.14
- 发布历史:
- 2024 年 10 月 9 日
摘要
本 PEP 提议禁止在 Python 的异常处理语法中使用裸露的 except:
子句。目前,Python 允许使用裸露的 except:
子句来捕获所有异常,这可能导致过于宽泛的异常处理并掩盖重要的错误。本 PEP 建议在所有 except 子句中要求明确的异常类型,以促进更精确和有意的错误处理。
动机
当前语法允许使用裸露的 except:
子句来捕获所有异常
try:
risky_operation()
except:
handle_any_error()
虽然这种语法对于“捕获所有”处理器来说可能很方便,但它经常导致糟糕的编码实践
- 它可以掩盖应该传播的重要错误。
- 通过捕获并可能隐藏意外的异常,使调试更加困难。
- 这违背了 Python “显式优于隐式”的原则。
各种 Linter [1] [2] [3] 和样式指南(包括 PEP 8)[4] [5] [6] [7] 都 discourage 裸露的 except
子句。
通过要求显式的异常类型,我们可以鼓励更周到和精确的错误处理
try:
risky_operation()
except Exception as e:
handle_expected_error(e)
这个问题的另一种观点是,裸露的 except 处理程序在处理终止异常的意图方面存在歧义,因为意图可能是
- 仅捕获非终止异常(
except Exception:
)。如果这是意图,那么使用裸露的except:
就是一个明显的错误,因为它不是这个意思。 - 捕获所有异常,包括终止异常(
except BaseException:
)。在这里使用裸露的except:
至少是正确的,但读者需要检查以确保它不是第一种情况的实例。
由于这两种可能的意图都有可用的无歧义的写法,因此模糊的形式是多余的,这就是我们建议禁止它的原因。
基本原理
禁止裸露 except 子句的决定基于以下考虑:
- 要求特定的异常类型使程序员的意图更加清晰,并鼓励思考可能发生的异常。
- 仅捕获特定异常可以更容易地识别和调试意外错误。
- 防止过于宽泛的异常处理可以降低静默忽略关键错误的风险。
- 许多样式指南和 Linter 已经 discourage 使用裸露的 except 子句。
规范
except 子句的语法将被修改为要求一个异常类型。语法将被更新,以删除在 except 子句中添加空表达式的可能性。
此更改禁止裸露的 except:
语法。所有 except 子句都必须指定至少一种异常类型
try:
...
except ValueError:
...
except (TypeError, RuntimeError):
...
except Exception:
... # Still allowed, but catches all exceptions explicitly
除了不再可能在未明确指定 BaseException
或类似广泛的异常类型的情况下捕获所有异常之外,异常处理的语义保持不变。
向后兼容性
此更改不向后兼容。使用裸露的 except:
子句的现有代码需要进行修改。为了便于过渡
- 将在 Python 3.14 中为裸露的 except 子句发出弃用警告。
- 该语法将在 Python 3.17 中被完全禁止。
- 将提供
from __future__ import strict_excepts
来使早期版本的 Python 中的裸露 except 处理程序无效。
将提供一个工具来自动更新代码,将裸露的 except:
替换为 except BaseException:
。
安全隐患
此更改没有安全方面的影响。
如何教授此内容
对于新的 Python 用户,应从一开始就以显式的异常类型来教授异常处理
try:
result = risky_operation()
except ValueError:
handle_value_error()
except TypeError:
handle_type_error()
except Exception as e:
handle_unexpected_error(e)
对于有经验的用户,可以将此更改作为一种最佳实践引入,该实践现在已被语言强制执行。应强调以下几点:
- 尽可能始终捕获特定的异常。
- 将
except Exception:
作为最后手段,用于真正意外的错误。 - 切勿在未仔细考虑的情况下抑制异常。
文档应指导常见的异常层次结构以及如何选择要捕获的适当异常类型。
被拒绝的想法
- 确实存在一些使用裸露
except:
处理程序的有效情况。Mailman [8] 中提出的一个例子涉及在任何异常情况下处理事务@contextmanager def transaction(): """Context manager for ensuring the transaction is complete.""" try: yield except: config.db.abort() raise else: config.db.commit()
这段代码保证,无论发生什么异常,任何未完成的事务都将被中止,而在成功的情况下,事务将被提交。
我们确实认为,尽管存在像这种情况这样使用裸露
except:
处理程序的正确情况,但出于“动机”部分中说明的原因,明确使用except BaseException:
会更好。
版权
本文档置于公共领域或 CC0-1.0-Universal 许可证下,以更宽松者为准。
来源:https://github.com/python/peps/blob/main/peps/pep-0760.rst