Following system colour scheme Selected dark colour scheme Selected light colour scheme

Python 增强提案

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 语句从四种形式减少到两种

  1. raise(无参数)用于在 except 代码块中重新引发活动异常。
  2. 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)

其中 EXCEPTIONBaseException 的子类或 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] 使用 raisethrow 修复程序 [5][6] 机械地处理。

将执行以下转换

  1. 零元和一元 raise 语句将保持不变。
  2. 二元 raise 语句将从
    raise E, V
    

    更改为

    raise E(V)
    

    二元 throw() 调用将从

    generator.throw(E, V)
    

    更改为

    generator.throw(E(V))
    

    有关此转换的注意事项,请参见第 5 点。

  3. 三元 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 点。

  4. 其中 E 是元组字面量的二元和三元 raise 语句可以使用 2to3raise 修复程序自动转换。其中 E 是非字面量元组(例如,函数调用的结果)的 raise 语句需要手动转换。
  5. 其中 E 是异常类且 V 是异常实例的二元和三元 raise 语句需要特别注意。这些情况分为两类
    1. 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)
      
    2. 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