2011-06-08 71 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',)) 
  • 如何剝離掉最外層的例外並重新評估早期的例外情況?
  • 在倫納特的例子中,解決這個問題是其中內/下/第一異常被捕獲並存儲在變量eexcept 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