摘要:下面我們來看一下效果下次需要捕獲一個異常然后再拋出另一個異常的時候大家可以試試本文的方法。
一般實現捕獲異常然后再拋出另一個異常的方法類似下面這樣:
def div(): 2 / 0 try: div() except ZeroDivisionError as e: raise ValueError(e)
不知道大家有沒有注意到這樣拋出異常的方式有一個很嚴重的問題,那就是 在重新拋出另一個異常的時候,捕獲的上一個異常的 traceback 信息丟失了(python2): :
$ cat a.py def div(): 2 / 0 try: div() except ZeroDivisionError as e: raise ValueError(e) $ python2 a.py Traceback (most recent call last): File "a.py", line 6, inraise ValueError(e) ValueError: integer division or modulo by zero
這樣的話非常不利于查找問題: 比如上面的例子中實際出錯的代碼是第二行,但是 當我們捕獲了第一個異常然后再拋出一個自定義異常的時候, 實際出錯位置的信息就丟失了。
Python 2那么在 Python 2 下如果我們不想丟失捕獲的異常的 traceback 信息的話,應該 怎樣重新拋出異常呢?
有兩種辦法, 還是用上面的例子舉例:
一種辦法是直接 raise: :
$ cat a.py def div(): 2 / 0 try: div() except ZeroDivisionError as e: raise $ python2 a.py Traceback (most recent call last): File "a.py", line 4, indiv() File "a.py", line 2, in div 2 / 0 ZeroDivisionError: integer division or modulo by zero
另一種辦法就是 raise 另一個異常時指定上一個異常的 traceback 信息 (通過 sys.exc_info() 獲取當前捕獲的異常信息): :
$ cat a.py import sys def div(): 2 / 0 try: div() except ZeroDivisionError as e: raise ValueError(e), None, sys.exc_info()[2] $ python2 a.py Traceback (most recent call last): File "a.py", line 6, indiv() File "a.py", line 4, in div 2 / 0 ValueError: integer division or modulo by zero
這個是 raise 的高級用法:
raise exception, value, traceback
exception: 異常類實例/異常類
value: 初始化異常類的參數值/異常類實例(使用這個實例作為 raise 的異常實例)/元組/None
traceback: traceback 對象/None
下面我們來看看上面的方法是否可以應對多層異常捕獲然后再拋出的情況: :
$ cat a.py import sys def div(): 2 / 0 def foo(): try: div() except ZeroDivisionError as e: raise ValueError(e), None, sys.exc_info()[2] def bar(): try: foo() except ValueError as e: raise TypeError(e), None, sys.exc_info()[2] def foobar(): try: bar() except TypeError as e: raise foobar() $ python2 a.py Traceback (most recent call last): File "a.py", line 23, infoobar() File "a.py", line 20, in foobar bar() File "a.py", line 14, in bar foo() File "a.py", line 8, in foo div() File "a.py", line 4, in div 2 / 0 TypeError: integer division or modulo by zero
從上面的結果可以看到這兩種方法是支持多層異常 traceback 信息傳遞的。
那么在 Python 3 下又怎么解決這個問題呢?
Python 3在 Python 3 下默認會附加上捕獲的上個異常的 trackback 信息(保存在異常實例的 __traceback__ 屬性中): :
$ cat a.py def div(): 2 / 0 try: div() except ZeroDivisionError as e: raise ValueError(e) $ python3 a.py Traceback (most recent call last): File "a.py", line 4, indiv() File "a.py", line 2, in div 2 / 0 ZeroDivisionError: division by zero During handling of the above exception, another exception occurred: Traceback (most recent call last): File "a.py", line 6, in raise ValueError(e) ValueError: division by zero
也支持指定使用哪個異常實例的 traceback 信息: raise ... from ... :
$ cat a.py def div(): 2 / 0 try: div() except ZeroDivisionError as e: raise ValueError(e) from e $ python a.py Traceback (most recent call last): File "a.py", line 5, indiv() File "a.py", line 2, in div 2 / 0 ZeroDivisionError: division by zero The above exception was the direct cause of the following exception: Traceback (most recent call last): File "a.py", line 7, in raise ValueError(e) from e ValueError: division by zero
也可以指定使用的 traceback 對象: raise exception.with_traceback(traceback) :
$ cat a.py import sys def div(): 2 / 0 try: div() except ZeroDivisionError as e: raise ValueError(e).with_traceback(sys.exc_info()[2]) $ python a.py Traceback (most recent call last): File "a.py", line 7, in兼容 Python 2 和 Python 3 的寫法div() File "a.py", line 4, in div 2 / 0 ZeroDivisionError: division by zero During handling of the above exception, another exception occurred: Traceback (most recent call last): File "a.py", line 9, in raise ValueError(e).with_traceback(sys.exc_info()[2]) File "a.py", line 7, in div() File "a.py", line 4, in div 2 / 0 ValueError: division by zero
上面介紹了在 Python 2 和 Python 3 下的不同解決辦法,那么如何寫一個兼容 Python 2 和 Python 3 的 reraise 函數呢?
下面將介紹一種方法:
PY3 = sys.version_info[0] == 3 if PY3: def reraise(tp, value, tb=None): if value.__traceback__ is not tb: raise value.with_traceback(tb) else: raise value else: exec("""def reraise(tp, value, tb=None): raise tp, value, tb """)
這里的 reraise 函數我們約定了 vlaue 參數的值是一個異常類的實例。 上面 else 中之所以用 exec 去定義 reraise 函數是因為 raise tp, value, tb 在 Python 3 下會報語法錯誤,所以用 exec 來 繞過 Python 3 下的語法錯誤檢查。
下面我們來看一下效果: :
$ cat a.py ef div(): 2 / 0 def foo(): try: div() except ZeroDivisionError as e: reraise(ValueError, ValueError(e), sys.exc_info()[2]) def bar(): try: foo() except ValueError as e: reraise(TypeError, TypeError(e), sys.exc_info()[2]) def foobar(): try: bar() except TypeError: raise foobar()
Python 2: :
$ python2 a.py Traceback (most recent call last): File "a.py", line 34, infoobar() File "a.py", line 31, in foobar bar() File "a.py", line 27, in bar reraise(TypeError, TypeError(e), sys.exc_info()[2]) File "a.py", line 25, in bar foo() File "a.py", line 21, in foo reraise(ValueError, ValueError(e), sys.exc_info()[2]) File "a.py", line 19, in foo div() File "a.py", line 15, in div 2 / 0 TypeError: integer division or modulo by zero
Python 3: :
$ python3 a.py Traceback (most recent call last): File "a.py", line 19, in foo div() File "a.py", line 15, in div 2 / 0 ZeroDivisionError: division by zero During handling of the above exception, another exception occurred: Traceback (most recent call last): File "a.py", line 25, in bar foo() File "a.py", line 21, in foo reraise(ValueError, ValueError(e), sys.exc_info()[2]) File "a.py", line 6, in reraise raise value.with_traceback(tb) File "a.py", line 19, in foo div() File "a.py", line 15, in div 2 / 0 ValueError: division by zero During handling of the above exception, another exception occurred: Traceback (most recent call last): File "a.py", line 34, infoobar() File "a.py", line 31, in foobar bar() File "a.py", line 27, in bar reraise(TypeError, TypeError(e), sys.exc_info()[2]) File "a.py", line 6, in reraise raise value.with_traceback(tb) File "a.py", line 25, in bar foo() File "a.py", line 21, in foo reraise(ValueError, ValueError(e), sys.exc_info()[2]) File "a.py", line 6, in reraise raise value.with_traceback(tb) File "a.py", line 19, in foo div() File "a.py", line 15, in div 2 / 0 TypeError: division by zero
下次需要捕獲一個異常然后再拋出另一個異常的時候大家可以試試本文的方法。
參考資料6. Simple statements — Python 2.7.12 documentation
6. Built-in Exceptions — Python 2.7.12 documentation
7. Simple statements — Python 3.5.2 documentation
5. Built-in Exceptions — Python 3.5.2 documentation
PEP 3109 -- Raising Exceptions in Python 3000 | Python.org
bottle/bottle.py at cafc15419cbb4a6cb748e6ecdccf92893bb25ce5 · bottlepy/bottle
flask/_compat.py at 6e46d0cd3969f6c13ff61c95c81a975192232fed · pallets/flask
原文地址: https://mozillazg.com/2016/08...
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://m.specialneedsforspecialkids.com/yun/38180.html
摘要:我們使用單元測試來驗證一下我們使用了配合做單元測試。我們編寫相應的單元測試你會發現,如果出現異常,只是簡單的返回。但是在上面異常拋出的時候,解釋器已經不在中了,因此無法被捕獲。 譯者按: 錯誤是無法避免的,妥善處理它才是最重要的! 原文: A Guide to Proper Error Handling in JavaScript Related Topics: 譯者: Funde...
摘要:一旦異常被拋出,就表明錯誤已無法挽回,也不能回來繼續執行。這種在編譯時被強制檢查的異常稱為被檢查的異常。通過獲取原始異常。構造器對于在構造階段可能會拋出異常,并要求清理的類,最安全的做法是使用嵌套的子句。 點擊進入我的博客 Java異常處理的目的在于通過使用少于目前數量的代碼來簡化大型、可靠的程序的生成,并且通過這種方式可以使你更自信:你的應用中沒有未處理的錯誤。 12.1 概念 異...
摘要:如何良好的在代碼中設計異常機制本身設計的出發點是極好的,通過編譯器的強制捕獲,可以明確提醒調用者處理異常情況。但使用此種異常后,該會像病毒一樣,得不到處理后會污染大量代碼,同時也可能因為調用者的不當處理,會失去異常信息。 1、異常是什么? 父類為Throwable,有Error和Exception兩個子類 Error為系統級別的異常(錯誤) Exception下有眾多子類,常見的有Ru...
摘要:對異常的處理方法是打印異常的跟蹤棧信息并終止程序運行。應盡量對異常進行適當的處理,而不是簡單的將異常跟蹤棧信息打印出來。 一、異常概述 開發者都希望所有錯誤都能在編譯階段被發現,就是試圖在運行程序之前排除所有錯誤,但這是不現實的,余下問題必須在運行期間得到解決。 Java將異常分為兩種:CheckedException和RuntimeException。其中,CheckedExcept...
閱讀 3270·2021-11-15 11:37
閱讀 1078·2021-11-02 14:45
閱讀 3902·2021-09-04 16:48
閱讀 3578·2019-08-30 15:55
閱讀 754·2019-08-23 17:53
閱讀 1000·2019-08-23 17:03
閱讀 2032·2019-08-23 16:43
閱讀 2188·2019-08-23 16:22