14.9 �获异常�抛出�外的异常¶

问题¶

你想�获一个异常�抛出�外一个��的异常,�时还得在异常回溯中�留两个异常的信�。

解决方案¶

为了链接异常,使用 raise from 语��代替简�的 raise 语�。 它会让你�时�留两个异常的信�。例如:

>>> def example():
...     try:
...             int('N/A')
...     except ValueError as e:
...             raise RuntimeError('A parsing error occurred') from e
...
>>> example()
Traceback (most recent call last):
  File "<stdin>", line 3, in example
ValueError: invalid literal for int() with base 10: 'N/A'

上�的异常是下�的异常产生的直接原因:

Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 5, in example
RuntimeError: A parsing error occurred
>>>

在回溯中�以看到,两个异常都被�获。 �想�获这样的异常,你�以使用一个简�的 except 语�。 �过,你还�以通过查看异常对象的 __cause__ 属性�跟踪异常链。例如:

try:
    example()
except RuntimeError as e:
    print("It didn't work:", e)

    if e.__cause__:
        print('Cause:', e.__cause__)

当在 except �中�有�外的异常被抛出时会导致一个��的异常链的出现。例如:

>>> def example2():
...     try:
...             int('N/A')
...     except ValueError as e:
...             print("Couldn't parse:", err)
...
>>>
>>> example2()
Traceback (most recent call last):
  File "<stdin>", line 3, in example2
ValueError: invalid literal for int() with base 10: 'N/A'

在处�上述异常的时候,�外一个异常�生了:

Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 5, in example2
NameError: global name 'err' is not defined
>>>

这个例�中,你�时获得了两个异常的信�,但是对异常的解释��。 这时候,NameError 异常被作为程�最终异常被抛出,而�是�于解�异常的直接回应中。

如果,你想忽略掉异常链,�使用 raise from None :

>>> def example3():
...     try:
...             int('N/A')
...     except ValueError:
...             raise RuntimeError('A parsing error occurred') from None
...
>>>
example3()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 5, in example3
RuntimeError: A parsing error occurred
>>>

讨论¶

在设计代�时,在�外一个 except 代��中使用 raise 语�的时候你�特别�心了。 大多数情况下,这� raise 语�都应该被改� raise from 语�。也就是说你应该使用下�这�形�:

try:
   ...
except SomeException as e:
   raise DifferentException() from e

这样�的原因是你应该显示的将原因链接起�。 也就是说,DifferentException 是直接从 SomeException �生而�。 这�关系�以从回溯结果中看出�。

如果你�下�这样写代�,你�然会得到一个链接异常, �过这个并没有很清晰的说明这个异常链到底是内部异常还是�个未知的编程错误。

try:
   ...
except SomeException:
   raise DifferentException()

当你使用 raise from 语�的�,就很清楚的表明抛出的是第二个异常。

最�一个例�中��异常链信�。 尽管��异常链信��利于回溯,�时它也丢失了很多有用的调试信�。 �过万事皆平等,有时候��留适当的信�也是很有用的。