在Python 3中,当已存在异常时引发异常

48 投票
5 回答
42425 浏览
提问于 2025-04-16 19:10

在下面的代码中,当第二个异常(B)被抛出时,我的第一个异常(A)会发生什么呢?

class A(Exception): pass
class B(Exception): pass

try:
    try:
        raise A('first')
    finally:
        raise B('second')
except X as c:
    print(c)

如果我用 X = A 来运行,结果是:

Traceback (most recent call last):
  File "raising_more_exceptions.py", line 6, in 
    raise A('first')
__main__.A: first

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "raising_more_exceptions.py", line 8, in 
    raise B('second')
__main__.B: second

但是如果 X = B,结果是:

second

我在想:

  1. 我的第一个异常去哪儿了?
  2. 为什么只有最外层的异常可以被捕获?
  3. 我该如何去掉最外层的异常,并重新抛出之前的异常?

这个问题特别针对Python 3,因为它的异常处理和Python 2有很大不同。

5 个回答

9

Python的异常处理一次只能处理一个错误。不过,异常对象和其他变量一样,也会受到变量规则和垃圾回收的影响。所以,如果你把异常对象保存在某个变量里,之后即使出现了其他错误,你也可以在需要的时候处理这个异常。

在你的情况中,当在“finally”语句中出现错误时,Python 3会先打印出第一个错误的详细信息,然后再打印第二个错误的信息,这样做是为了更好地帮助你理解问题。

更常见的情况是,你可能想在处理一个错误的过程中再抛出一个新的错误。这时,你可以把第一个错误“保存”到下一个错误中。只需要把它作为参数传递进去:

>>> class A(Exception):
...     pass
... 
>>> class B(Exception):
...     pass
... 
>>> try:
...     try:
...         raise A('first')
...     except A as e:
...         raise B('second', e)
... except Exception as c:
...     print(c.args[1])
... 
first

如你所见,现在你可以访问到最初的那个错误了。

32

针对问题3,你可以使用:

raise B('second') from None

这样做会去掉异常A的错误追踪信息。

Traceback (most recent call last):
  File "raising_more_exceptions.py", line 8, in 
    raise B('second')
__main__.B: second
16

在你最后的异常处理器中,可以通过 c.__context__ 来获取“导致”异常的信息。考虑一下你的第二个例子,其中 X = B(在下面的例子中把 X 替换成了 B):

try:
    try:
        raise A('first')
    finally:
        raise B('second')
except B as c:
    print(repr(c))
    print(repr(c.__context__))

这将输出:

B('second')
A('first')

注意,c.__context__ 指向 A('first'),也就是第一个异常发生的地方。

Python 利用这些信息来生成更有用的错误追踪信息。在 Python 2.x 中,原始的异常信息会丢失,而这在 Python 3 中是可以保留的。

通常情况下,你会用这个方法来抛出一个一致的异常,同时还能保留原始异常的信息(虽然从异常处理器自动发生这一点真的很酷,我之前不知道!):

try:
    do_something_involving_http()
except (URLError, socket.timeout) as ex:
    raise MyError('Network error') from ex

更多信息(以及一些其他非常有用的功能)可以在这里找到: http://docs.python.org/3.3/library/exceptions.html

撰写回答