PEP 3109 – 在 Python 3000 中引发异常
- 作者:
- Collin Winter <collinwinter at google.com>
- 状态:
- 最终
- 类型:
- 标准跟踪
- 创建:
- 2006年1月19日
- Python 版本:
- 3.0
- 发布历史:
摘要
本 PEP 引入了对 Python 引发异常机制的更改,旨在减少代码冗余并缩减语言规模。
基本原理
Python 的指导原则之一是 “应该有一种——最好只有一种——显而易见的方法来做这件事”。Python 2.x 的 raise
语句违反了这一原则,允许用多种方式表达相同的含义。例如,以下语句是等价的
raise E, V
raise E(V)
raise
语句还有第三种形式,允许将任意回溯附加到异常 [1]
raise E, V, T
其中 T 是一个回溯。如 PEP 344 中所述,Python 3.x 中的异常对象将拥有一个 __traceback__
属性,允许对三表达式 raise
语句进行以下转换
raise E, V, T
转换为
e = E(V)
e.__traceback__ = T
raise e
使用这些转换,我们可以将 raise
语句从四种形式减少到两种
raise
(无参数)用于在except
代码块中重新引发活动异常。raise EXCEPTION
用于引发新的异常。此形式有两个子变体:EXCEPTION
可以是异常类或异常类的实例;有效的异常类是 BaseException 及其子类(PEP 352)。如果EXCEPTION
是一个子类,则将不带参数调用它以获取异常实例。引发任何其他内容都是错误的。
正如 A.M. Kuchling 所指出的 [2],通过这种合并可以获得进一步的、更实际的好处。
PEP 8 doesn't express any preference between the
two forms of raise statements:
raise ValueError, 'blah'
raise ValueError("blah")
I like the second form better, because if the exception arguments
are long or include string formatting, you don't need to use line
continuation characters because of the containing parens.
BDFL 已同意 [3] 并认可了几种 raise
形式的合并。
语法更改
在 Python 3 中,raise
语句的语法将从 [1]
raise_stmt: 'raise' [test [',' test [',' test]]]
更改为
raise_stmt: 'raise' [test]
对内置类型的更改
由于其与异常引发的关系,生成器对象上 throw()
方法的签名将发生变化,删除可选的第二个和第三个参数。因此,签名发生了变化(PEP 342):从
generator.throw(E, [V, [T]])
更改为
generator.throw(EXCEPTION)
其中 EXCEPTION
是 BaseException
的子类或 BaseException
的子类的实例。
语义更改
在 Python 2 中,以下 raise
语句是合法的
raise ((E1, (E2, E3)), E4), V
解释器将把元组的第一个元素作为异常类型(递归),使上述语句完全等价于
raise E1, V
从 Python 3.0 开始,将删除对这样引发元组的支持。此更改将使 raise
语句与生成器对象上的 throw()
方法保持一致,后者已经不允许这样做。
兼容性问题
所有二元和三元 raise
语句都需要修改,所有二元和三元 throw()
对生成器的调用也需要修改。幸运的是,在这种情况下,从 Python 2.x 到 Python 3.x 的转换很简单,并且可以通过 Guido van Rossum 的 2to3 实用程序 [4] 使用 raise
和 throw
修复程序 [5]、[6] 机械地处理。
将执行以下转换
- 零元和一元
raise
语句将保持不变。 - 二元
raise
语句将从raise E, V
更改为
raise E(V)
二元
throw()
调用将从generator.throw(E, V)
更改为
generator.throw(E(V))
有关此转换的注意事项,请参见第 5 点。
- 三元
raise
语句将从raise E, V, T
更改为
e = E(V) e.__traceback__ = T raise e
三元
throw()
调用将从generator.throw(E, V, T)
更改为
e = E(V) e.__traceback__ = T generator.throw(e)
有关此转换的注意事项,请参见第 5 点。
- 其中
E
是元组字面量的二元和三元raise
语句可以使用2to3
的raise
修复程序自动转换。其中E
是非字面量元组(例如,函数调用的结果)的raise
语句需要手动转换。 - 其中
E
是异常类且V
是异常实例的二元和三元raise
语句需要特别注意。这些情况分为两类raise E, V
作为零参数raise
语句的长格式版本。例如,假设 F 是 E 的子类try: something() except F as V: raise F(V) except E as V: handle(V)
最好表示为
try: something() except F: raise except E as V: handle(V)
raise E, V
作为将异常“转换”为另一个类的途径。从 distutils.compiler.unixcompiler 中举一个例子try: self.spawn(pp_args) except DistutilsExecError as msg: raise CompileError(msg)
最好表示为
try: self.spawn(pp_args) except DistutilsExecError as msg: raise CompileError from msg
使用 PEP 344 中引入的
raise ... from ...
语法。
实现
此 PEP 在修订版 57783 中实现 [7]。
参考文献
版权
本文档已进入公有领域。
来源:https://github.com/python/peps/blob/main/peps/pep-3109.rst
上次修改时间:2023年9月9日17:39:29 GMT