這是Debian Squeeze上的Python 2.6.6(默認)。考慮下面的Python代碼。在主程序或清理過程中發生錯誤時的異常處理
import sys
try:
raise Exception("error in main")
pass
except:
exc_info = sys.exc_info()
finally:
try:
print "cleanup - always run"
raise Exception("error in cleanup")
except:
import traceback
print >> sys.stderr, "Error in cleanup"
traceback.print_exc()
if 'exc_info' in locals():
raise exc_info[0], exc_info[1], exc_info[2]
print "exited normally"
得到的誤差是
Error in cleanup
Traceback (most recent call last):
File "<stdin>", line 10, in <module>
Exception: error in cleanup
cleanup - always run
Traceback (most recent call last):
File "<stdin>", line 3, in <module>
Exception: error in main
我們的想法是,以應付的情況下或者某些代碼或代碼的清理(總是運行)或兩者,給出了一個錯誤。對此有一些討論,例如,Ian Bicking在Re-raising Exceptions。在這篇文章的最後(見Update:
),他描述瞭如何處理類似的代碼+回滾/回覆(僅在出錯時運行)。
我擺弄這個,想出了上面的代碼,這是一個怪胎。特別是,如果只有 清除(註釋掉raise Exception("error in main")
)中的錯誤,但代碼仍然正常退出,但它確實打印出了回溯。目前,我正在給予非清理錯誤優先級,所以它會停止程序。
理想情況下,我想要任何錯誤停止該程序,但這似乎並不容易安排。 Python似乎只想提出一個錯誤,如果有的話會丟失其他錯誤,默認情況下它通常是最後一個錯誤。重新排列這會產生像上面那樣的卷積。
另外使用locals()
有點難看。一個人能做得更好嗎?
編輯:srgerg's answer向我介紹了上下文管理器和with
關鍵字的概念。除了PEP 343之外,我找到的其他相關文檔都是(無特定順序)。 Context Manager Types,The with statement和http://docs.python.org/reference/datamodel.html#context-managers。這對於以前的方法來說似乎是一個很大的改進,即涉及trys,excepts和finallys的意大利麪代碼。
總而言之,我想要這樣的解決方案給我兩件事。
在其軌道到停止程序的能力爲在任一主代碼或在 清理異常。上下文管理器執行此操作,因爲如果with循環的主體出現異常並且主體 出口沒有,則會傳播該異常。 如果退出拋出一個異常,並且with循環的主體沒有, 然後傳播。如果兩者都拋出異常,則出口 異常被傳播,並且來自while循環體的一個被抑制,即 。這全部記錄在案,即從 Context Manager Types,
contextmanager。 exit(exc_type,exc_val,exc_tb)
退出運行環境並返回一個布爾標誌,指示是否應該抑制發生的任何異常。 [...] 從此方法返回真值將導致with語句抑制異常,並在with語句後立即繼續執行 語句。否則,此方法執行完畢 後異常繼續傳播。在執行此方法期間發生的異常將替換髮生在
語句正文中的任何異常。 [...]通過的例外不應該明確重新考慮。相反,此方法應返回一個假值爲 ,表示該方法已成功完成,並且不希望抑制引發的異常。如果在這兩個地方例外,我希望看到回溯從兩個 ,即使在技術上只有一個異常被拋出。這是真正的 基於實驗的,因爲如果兩個拋出一個異常, 那麼出口異常傳播,但是從while循環體 回溯仍印刷,如 srgerg's answer。 但是,我無法在任何地方找到此記錄的 ,這是不令人滿意的。
import sys, traceback def excepthook(*exc_info): print "cleanup - always run" raise Exception("error in cleanup") traceback.print_exception(*exc_info) sys.excepthook = excepthook raise Exception("error in main")
輸出示例::
謝謝,對我來說這是一個全新的想法。是否有清除你提出的異常(「在__exit __期間發生異常」)的想法?如果是這樣,你可以添加一個合適的'打印'清理 - 總是運行「'或類似的? – 2012-01-02 09:28:01
我已經按要求添加了打印語句。 – srgerg 2012-01-02 09:43:32
謝謝,srgerg。我正在閱讀PEP。我認爲我以前沒有聽說過或看過「with」關鍵字,但也許我只是沒有注意。將一個清理代碼放入輸入或退出函數中,還是不會產生影響? – 2012-01-02 09:45:48