簡體   English   中英

python如何重新引發已經捕獲的異常?

[英]python how to re-raise an exception which is already caught?

import sys
def worker(a):
    try:
        return 1 / a
    except ZeroDivisionError:
        return None


def master():
    res = worker(0)
    if not res:
        print(sys.exc_info())
        raise sys.exc_info()[0]

作為上面的代碼,我有很多類似worker的函數。 他們已經有自己的try-except塊來處理異常。 然后,一個主函數將調用每個工作者。 現在,sys.exc_info()將所有None返回3個元素,如何在master函數中重新引發異常? 我正在使用Python 2.7

一個更新:我有1000多名工人,有些工人邏輯非常復雜,他們可能同時處理多種類型的異常。 所以我的問題是,我可以只提出來自master的例外而不是編輯作品嗎?

未經測試,但我懷疑您可以做這樣的事情。 根據變量的范圍,您必須對其進行更改,但我想您會明白的

try:
    something
except Exception as e:
    variable_to_make_exception = e

.....稍后使用變量

使用這種方式處理錯誤的示例:

errors = {}
try:
    print(foo)
except Exception as e:
    errors['foo'] = e
try:
    print(bar)
except Exception as e:
    errors['bar'] = e


print(errors)
raise errors['foo']

輸出..

{'foo': NameError("name 'foo' is not defined",), 'bar': NameError("name 'bar' is not defined",)}
Traceback (most recent call last):
  File "<input>", line 13, in <module>
  File "<input>", line 3, in <module>
NameError: name 'foo' is not defined

您嘗試執行的操作無效。 一旦處理了異常(不重新引發),該異常及其伴隨的狀態便被清除,因此無法訪問它。 如果要讓異常有效,則必須不處理它,或手動使其保持活動狀態。

這在文檔中並不是那么容易找到(關於CPython的底層實現細節要容易一些,但是理想情況下我們想知道該語言定義的Python),但是它存在於except引用except中:

…這意味着必須將異常分配給其他名稱,以便能夠在except子句之后引用該異常。 清除異常是因為它們具有附加的回溯,它們與堆棧框架形成了一個參考循環,使該框架中的所有本地對象都保持活動狀態,直到發生下一個垃圾回收為止。

在執行except子句的套件之前,有關異常的詳細信息存儲在sys模塊中,並且可以通過sys.exc_info()進行訪問。 sys.exc_info()返回一個sys.exc_info()組,該sys.exc_info()組由異常類,異常實例和回溯對象(請參閱標准類型層次結構)組成,用於標識程序中發生異常的點。 從處理異常的函數返回時, sys.exc_info()值將還原為先前的值(在調用之前)。

同樣,這確實是異常處理程序的重點:當一個函數處理異常時,對於該函數外部的世界,似乎沒有異常發生。 這在Python中比在許多其他語言中更為重要,因為Python如此混雜地使用異常-每個for循環,每個hasattr調用等都引發和處理異常,並且您不希望看到它們。


因此,執行此操作的最簡單方法是僅更改工作程序以不處理異常(或先記錄日志然后重新引發它們,或執行其他操作),然后讓異常處理按預期的方式工作。

在某些情況下,您無法執行此操作。 例如,如果您的實際代碼在后台線程中運行工作程序,則調用者將看不到異常。 在這種情況下,您需要手動將其傳遞回去。 舉一個簡單的例子,讓我們更改工作函數的API以返回值和異常:

def worker(a):
    try:
        return 1 / a, None
    except ZeroDivisionError as e:
        return None, e

def master():
    res, e = worker(0)
    if e:
        print(e)
        raise e

顯然,您可以進一步擴展此范圍,以返回整個exc_info三元組,或者您想要的任何其他內容; 對於示例,我只是要使其盡可能簡單。

如果您查看concurrent.futures類之類的東西,這就是它們如何處理將線程或進程池上運行的任務中的異常傳遞回父級的方式(例如,當您等待Future )。


如果您不能修改工作程序,那么您基本上就沒有運氣了。 當然,您可以編寫一些可怕的代碼在運行時修補工人(通過使用inspect獲取其源代碼,然后使用ast進行解析,轉換和重新編譯,或者直接深入到字節碼中),但這是對於任何類型的生產代碼,幾乎從來都不是一個好主意。

在您的情況下, worker的異常返回None 一旦發生這種情況,就不會再有異常了。 如果你的主函數知道返回值應該是什么每個功能(例如, ZeroDivisionErrorworker reutrns None ,你可以手動再加注例外。

如果您不能自己編輯輔助函數,那么我認為您無能為力。 如果它們可以在代碼中以及在控制台上工作,則您可能可以使用此答案中的某些解決方案

上面的krflol代碼類似於C處理異常的方式-有一個全局變量,每當發生異常時,就會分配一個數字,該數字以后可以交叉引用以找出異常是什么。 這也是可能的解決方案。

但是,如果您願意編輯輔助函數,那么將異常升級為調用該函數的代碼實際上非常簡單:

try: 
    # some code
except:
    # some response
    raise

如果在catch塊的末尾使用空白raise ,則會重新引發剛捕獲的異常。 另外,如果您需要調試打印,執行相同的操作,甚至引發其他異常,則可以命名該異常。

except Exception as e:
    # some code
    raise e

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM