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