PEP 3110 – 在 Python 3000 中捕获异常
- 作者:
- Collin Winter <collinwinter at google.com>
- 状态:
- 最终版
- 类型:
- 标准跟踪
- 创建日期:
- 2006年1月16日
- Python 版本:
- 3.0
- 发布历史:
摘要
本PEP引入的更改旨在帮助消除Python语法中的歧义,简化异常类,简化异常的垃圾回收,并减小Python 3.0中语言的大小。
基本原理
- Python 2.x 中的
except子句存在语法歧义,解析器无法区分except <expression>, <expression>:
应该被解释为
except <type>, <type>:
或
except <type>, <name>:
Python 2 选择后一种语义,代价是要求前者加括号,如下所示
except (<type>, <type>):
- 正如 PEP 352 中所规定的,将移除将异常视为元组的能力,这意味着此代码将不再工作
except os.error, (errno, errstr):
因为自动解包将不再可能,所以希望移除使用元组作为
except目标的能力。 - 正如 PEP 344 中所规定的,Python 3 中的异常实例将拥有一个
__traceback__属性。该 PEP 的“开放问题”部分包含一段关于此属性导致的垃圾回收困难的段落,即“异常 -> 回溯 -> 栈帧 -> 异常”的引用循环,所有局部变量都将保留在作用域内,直到下一次垃圾回收运行。本 PEP 旨在通过在 Python 3 的except子句中添加清理语义来解决此问题,即在except块结束时删除目标名称。 - 本着 “应该有一种——最好只有一种——显而易见的方法来做这件事” 的精神,合并重复的功能是可取的。为此,
sys模块的exc_value、exc_type和exc_traceback属性 [1] 将被删除,转而使用提供相同信息的sys.exc_info()。这些属性已在 PEP 3100 中列为目标删除项。
语法变更
在 Python 3 中,except 语句的语法将从 [4] 变为
except_clause: 'except' [test [',' test]]
到
except_clause: 'except' [test ['as' NAME]]
使用 as 代替逗号标记意味着
except (AttributeError, os.error):
可以清楚地理解为异常类的元组。这种新语法最初由 Greg Ewing 提出 [2] 并得到 BDFL 的认可 ([2], [3])。
此外,as 后面的 token 从 test 限制为 NAME 意味着只有有效的标识符才能用作 except 目标。
请注意,上述语法总是要求将元组用括号括起来作为异常类。这样,模糊的
except A, B:
它在 Python 2.x 和 3.x 中将意味着不同的东西——导致难以捕获的错误——在 3.x 代码中是不能合法发生的。
语义变更
为了解决与 PEP 344 相关的垃圾回收问题,Python 3 中的 except 语句将生成额外的字节码来删除目标,从而消除引用循环。Phillip J. Eby 建议的源代码到源代码的转换 [5] 是
try:
try_body
except E as N:
except_body
...
被翻译成(用 Python 2.5 的术语来说)
try:
try_body
except E, N:
try:
except_body
finally:
N = None
del N
...
一个实现已经被检查到 py3k(以前是“p3yk”)分支 [6] 中。
兼容性问题
几乎所有 except 子句都需要更改。带有标识符目标的 except 子句将从
except E, N:
到
except E as N:
带有非元组、非标识符目标(例如,a.b.c[d])的 except 子句需要从
except E, T:
到
except E as t:
T = t
这两种情况都可以通过 Guido van Rossum 的 2to3 工具 [7] 使用 except 修复器 [8] 来处理。
带有元组目标的 except 子句需要手动逐案转换。这些更改通常需要伴随异常类本身的更改。虽然这些更改通常无法自动化,但 2to3 工具能够指出 except 子句的目标是元组的情况,从而简化转换。
如果需要在 except 块结束之后保留异常实例,可以很容易地按如下方式转换:
try:
...
except E as N:
...
...
变成
try:
...
except E as N:
n = N
...
...
这样,当 N 在块结束时被删除时,n 将继续存在并可正常使用。
最后,所有使用 sys 模块的 exc_type、exc_value 和 exc_traceback 属性的地方都将被删除。它们可以分别替换为 sys.exc_info()[0]、sys.exc_info()[1] 和 sys.exc_info()[2],这种转换可以通过 2to3 的 sysexcattrs 修复器执行。
2.6 - 3.0 兼容性
为了方便 Python 2.6 和 3.0 之间的向前兼容,except ... as ...: 语法将反向移植到 2.x 系列。语法将因此从
except_clause: 'except' [test [',' test]]
到
except_clause: 'except' [test [('as' | ',') test]]
except 语句的“块结束清理”语义将不会包含在 2.x 系列版本中。
未解决的问题
替换或删除“sys.exc_info()”
关于放弃 sys.exc_info() 或将其替换为 sys.exception 属性或 sys.get_exception() 函数的想法已在 python-3000 上多次提出 ([9], [10]),并在 PEP 344 的“开放问题”部分提及。
虽然一个 2to3 修复器可以轻易地替换对 sys.exc_info() 的调用和一些属性访问,但对于静态分析来说,要找到并修复将 sys.exc_info() 的值作为参数的函数会困难得多。同样,这并没有解决需要重写所有以 sys.exc_info() 定义的 API 文档的需求。
实施
本 PEP 在修订版 53342 [11] 和 53349 [12] 中实现。对 2.6 中新 except 语法的支持在修订版 55446 [13] 中实现。
参考资料
版权
本文档已置于公共领域。
来源:https://github.com/python/peps/blob/main/peps/pep-3110.rst