在Python 3中,当已存在异常时引发异常
在下面的代码中,当第二个异常(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
我在想:
- 我的第一个异常去哪儿了?
- 为什么只有最外层的异常可以被捕获?
- 我该如何去掉最外层的异常,并重新抛出之前的异常?
这个问题特别针对Python 3,因为它的异常处理和Python 2有很大不同。
5 个回答
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
如你所见,现在你可以访问到最初的那个错误了。
针对问题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
在你最后的异常处理器中,可以通过 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