Python 简明教程

Python - Exception Chaining

Exception Chaining

异常链是一个通过在新的异常中封装捕获的异常后重新引发捕获的异常来处理异常的技术。原始异常被保存为新异常的属性(例如cause)。

Exception chaining is a technique of handling exceptions by re-throwing a caught exception after wrapping it inside a new exception. The original exception is saved as a property (such as cause) of the new exception.

在处理一个异常“A”期间,可能会发生另一个异常“B”。了解这两个异常对于调试问题很有用。有时对于异常处理程序故意重新引发异常是有用的,以提供额外的信息或将异常转换为另一种类型。

During the handling of one exception 'A', it is possible that another exception 'B' may occur. It is useful to know about both exceptions in order to debug the problem. Sometimes it is useful for an exception handler to deliberately re-raise an exception, either to provide extra information or to translate an exception to another type.

在Python 3.x中,可以实现异常链。如果except部分内有任何未处理的异常,它将处理正在处理的异常附加到它并包含在错误消息中。

In Python 3.x, it is possible to implement exception chaining. If there is any unhandled exception inside an except section, it will have the exception being handled attached to it and included in the error message.

Example

在下面的代码片段中,尝试打开一个不存在的文件会引发FileNotFoundError。它是由except块检测到的。在处理时引发了另一个异常。

In the following code snippet, trying to open a non-existent file raises FileNotFoundError. It is detected by the except block. While handling another exception is raised.

try:
   open("nofile.txt")
except OSError:
   raise RuntimeError("unable to handle error")

它将生成以下 output

It will produce the following output

Traceback (most recent call last):
  File "/home/cg/root/64afcad39c651/main.py", line 2, in <module>
open("nofile.txt")
FileNotFoundError: [Errno 2] No such file or directory: 'nofile.txt'

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/home/cg/root/64afcad39c651/main.py", line 4, in <module>
    raise RuntimeError("unable to handle error")
RuntimeError: unable to handle error

The raise . . from Statement

如果在raise语句中使用可选的from子句,则表示异常是另一个异常的直接后果。当转换异常时,这一点很有用。from关键字之后的标记应该是异常对象。

If you use an optional from clause in the raise statement, it indicates that an exception is a direct consequence of another. This can be useful when you are transforming exceptions. The token after from keyword should be the exception object.

try:
   open("nofile.txt")
except OSError as exc:
   raise RuntimeError from exc

它将生成以下 output

It will produce the following output

Traceback (most recent call last):
  File "/home/cg/root/64afcad39c651/main.py", line 2, in <module>
    open("nofile.txt")
FileNotFoundError: [Errno 2] No such file or directory: 'nofile.txt'

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "/home/cg/root/64afcad39c651/main.py", line 4, in <module>
    raise RuntimeError from exc
RuntimeError

The raise . . from None Statement

如果我们在from子句中使用None代替异常对象,则会禁用较早示例中找到的自动异常链。

If we use None in from clause instead of exception object, the automatic exception chaining that was found in the earlier example is disabled.

try:
   open("nofile.txt")
except OSError as exc:
   raise RuntimeError from None

它将生成以下 output

It will produce the following output

Traceback (most recent call last):
 File "C:\Python311\hello.py", line 4, in <module>
  raise RuntimeError from None
RuntimeError

The context and cause Expression

在except块中引发异常会自动将捕获的异常添加到新异常的 context 属性。同样,还可以使用 raise …​ from 语法向任何异常添加 cause

Raising an exception in the except block will automatically add the captured exception to the context attribute of the new exception. Similarly, you can also add cause to any exception using the expression raise …​ from syntax.

try:
   try:
      raise ValueError("ValueError")
   except ValueError as e1:
      raise TypeError("TypeError") from e1
except TypeError as e2:
   print("The exception was", repr(e2))
   print("Its __context__ was", repr(e2.__context__))
   print("Its __cause__ was", repr(e2.__cause__))

它将生成以下 output

It will produce the following output

The exception was TypeError('TypeError')
Its __context__ was ValueError('ValueError')
Its __cause__ was ValueError('ValueError')