PEP 3110 – Python 3000 中的异常捕获
- 作者:
- Collin Winter <collinwinter at google.com>
- 状态:
- 最终
- 类型:
- 标准跟踪
- 创建:
- 2006年1月16日
- Python 版本:
- 3.0
- 历史记录:
摘要
本 PEP 引入了旨在帮助消除 Python 语法中的歧义、简化异常类、简化异常的垃圾回收以及减少 Python 3.0 中语言规模的更改。
基本原理
except
子句在 Python 2.x 中存在语法歧义,解析器无法区分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 的“未解决的问题”部分包含一段关于此属性引起的垃圾回收困难的段落,即“异常 -> 回溯 -> 栈帧 -> 异常”引用循环,其中所有局部变量都保留在范围内,直到下一次 GC 运行。本 PEP 旨在通过在 Python 3 中的except
子句中添加清理语义来解决此问题,其中目标名称在except
代码块的末尾被删除。 - 本着 “应该有一种——最好只有一种——显而易见的方法来做这件事” 的精神,需要整合重复的功能。为此,
sys
模块的 [1]exc_value
、exc_type
和exc_traceback
属性将被删除,以支持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
后面的标记从 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:
except
子句,其目标不是元组,也不是标识符(例如,a.b.c[d]
)需要从
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 ... as ...:
语法。2.x 系列的版本中将不包含 except
语句的代码块末尾清理语义。
未解决的问题
替换或删除“sys.exc_info()”
在 python-3000 上多次提出删除 sys.exc_info()
或将其替换为 sys.exception
属性或 sys.get_exception()
函数的想法([9]、[10]),并在 PEP 344 的“未解决的问题”部分中提到。
虽然用于替换对 sys.exc_info()
的调用和某些属性访问的 2to3
修复程序将微不足道,但对于静态分析来说,找到并修复将 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