2011-06-08 74 views
26

当在以下代码中引发第二个异常(B)时,我的第一个异常(A)会发生什么?在Python 3中已经存在异常时引发异常

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. 如何剥离最外层的异常并重新评估先前的异常?

Update0

这个问题专门针对Python 3中,因为它的异常处理是对Python 2

+0

答案似乎忽略了这样一个事实,即当没有发现异常时我仍然得到完整的回溯。请解释? – 2011-06-08 14:46:35

回答

6
  1. 它得到抛出了很大的不同。
  2. 每个线程一次只能有一个“活动”异常。
  3. 你不能,除非你以某种方式在后面的异常中封装了前面的异常。
+1

如果它被抛出,为什么当异常未被捕获时我得到完整的回溯? – 2011-06-10 02:14:34

+0

因为您用来运行脚本的工具已经安装了全局异常处理程序,并且它已经注意到了双重异常。 – 2011-06-10 03:06:28

+1

什么工具.....? – 2011-06-10 03:32:35

8

pythons异常处理一次只能处理一个异常。但是,异常对象与其他所有变量规则和垃圾收集相同。因此,如果将异常对象保存在某个可以在以后处理的变量中,即使引发了另一个异常。

就你而言,当在“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 

正如您所看到的,您现在可以访问原始异常。

+0

你能回答第1部分吗? – 2011-06-10 02:15:17

+0

@Matt:它不会去任何地方。不过,我确实意识到我有一个脑力激荡并更新了我的答案。 – 2011-06-10 05:29:15

+0

在文档测试中如何处理这种情况? – hayavuk 2014-06-18 12:55:32

5

我相信所有的成分来回答你的问题已经在现有的答案。让我结合和阐述。

让我再说一遍你的问题的代码提供行号引用:

1 class A(Exception): pass 
2 class B(Exception): pass 
3 
4 try: 
5  try: 
6   raise A('first') 
7  finally: 
8   raise B('second') 
9 except X as c: 
10  print(c) 

因此,要回答你的问题:

  1. 在哪里我的第一个例外走?

您的第一个异常A在第6行中提出。第7行中的finally子句为始终为,只要try块(第5-6行)留下,无论是由于成功完成还是因为引发异常而留下。 虽然正在执行finally子句,但第8行引发了另一个异常B。正如Lennart和Ignazio指出的那样,只有一个例外,最近被提出的例外,可以跟踪。因此,只要B上升,则总体try块(第4-8行)将退出,并且异常B正在被第9行中的except语句捕捉,如果它匹配(如果XB)。

  1. 为什么只有最外层的异常是可捕获的?

希望这从我对1的解释中已经清楚了。尽管如此,您可以捕获内部/下部/第一个异常。在梅里的答案合并,略作修改,这里是如何抓住两个:

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) 

输出是:

('second', A('first',)) 
  • 如何剥离掉最外层的例外并重新评估早期的例外情况?
  • 在伦纳特的例子中,解决这个问题是其中内/下/第一异常被捕获并存储在变量e线except A as e

    作为一种普遍的直觉,什么时候要抓住例外,什么时候忽视它们,什么时候重新加薪,或许this question and Alex Martelli's answer的帮助。

    7

    在您的上一个异常处理程序中,“引发”异常可用作c .__ context__。 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

    3

    回答问题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