異常處理在任何一門編程語言里都是值得關注的一個話題,良好的異常處理可以讓你的程序更加健壯,清晰的錯誤信息更能幫助你快速修復問題。在Python中,和不分高級語言一樣,使用了try/except/finally語句塊來處理異常,如果你有其他編程語言的經驗,實踐起來并不難。
什么是異常?
1.錯誤
從軟件方面來說,錯誤是語法或是邏輯上的。錯誤是語法或是邏輯上的。
語法錯誤指示軟件的結構上有錯誤,導致不能被解釋器解釋或編譯器無法編譯。這些些錯誤必須在程序執行前糾正。
當程序的語法正確后,剩下的就是邏輯錯誤了。邏輯錯誤可能是由于不完整或是不合法的輸入所致;
在其它情況下,還可能是邏輯無法生成、計算、或是輸出結果需要的過程無法執行。這些錯誤通常分別被稱為域錯誤和范圍錯誤。
當python檢測到一個錯誤時,python解釋器就會指出當前流已經無法繼續執行下去。這時候就出現了異常。
2.異常
對異常的最好描述是:它是因為程序出現了錯誤而在正常控制流以外采取的行為。
這個行為又分為兩個階段:首先是引起異常發生的錯誤,然后是檢測(和采取可能的措施)階段。
第一階段是在發生了一個異常條件(有時候也叫做例外的條件)后發生的。
只要檢測到錯誤并且意識到異常條件,解釋器就會發生一個異常。引發也可以叫做觸發,拋出或者生成。解釋器通過它通知當前控制流有錯誤發生。
python也允許程序員自己引發異常。無論是python解釋器還是程序員引發的,異常就是錯誤發生的信號。
當前流將被打斷,用來處理這個錯誤并采取相應的操作。這就是第二階段。
對于異常的處理發生在第二階段,異常引發后,可以調用很多不同的操作。
可以是忽略錯誤(記錄錯誤但不采取任何措施,采取補救措施后終止程序。)或是減輕問題的影響后設法繼續執行程序。
所有的這些操作都代表一種繼續,或是控制的分支。關鍵是程序員在錯誤發生時可以指示程序如何執行。
python用異常對象(exception object)來表示異常。遇到錯誤后,會引發異常。
如果異常對象并未被處理或捕捉,程序就會用所謂的回溯(traceback)終止執行
異常處理
捕捉異常可以使用try/except語句。
try/except語句用來檢測try語句塊中的錯誤,從而讓except語句捕獲異常信息并處理。
如果你不想在異常發生時結束你的程序,只需在try里捕獲它。
語法:
以下為簡單的try....except...else的語法:
Try的工作原理是,當開始一個try語句后,python就在當前程序的上下文中作標記,這樣當異常出現時就可以回到這里,try子句先執行,接下來會發生什么依賴于執行時是否出現異常。
如果當try后的語句執行時發生異常,python就跳回到try并執行第一個匹配該異常的except子句,異常處理完畢,控制流就通過整個try語句(除非在處理異常時又引發新的異常)。
如果在try后的語句里發生了異常,卻沒有匹配的except子句,異常將被遞交到上層的try,或者到程序的最上層(這樣將結束程序,并打印缺省的出錯信息)。
如果在try子句執行時沒有發生異常,python將執行else語句后的語句(如果有else的話),然后控制流通過整個try語句。
使用except而不帶任何異常類型
你可以不帶任何異常類型使用except,如下實例:
以上方式try-except語句捕獲所有發生的異常。但這不是一個很好的方式,我們不能通過該程序識別出具體的異常信息。因為它捕獲所有的異常。
使用except而帶多種異常類型
你也可以使用相同的except語句來處理多個異常信息,如下所示:
try-finally 語句
try-finally 語句無論是否發生異常都將執行最后的代碼。
當在try塊中拋出一個異常,立即執行finally塊代碼。
finally塊中的所有語句執行后,異常被再次觸發,并執行except塊代碼。
參數的內容不同于異常。
下面來看一個實例:
點擊查看大圖
總結如下:
except語句不是必須的,finally語句也不是必須的,但是二者必須要有一個,否則就沒有try的意義了。
except語句可以有多個,Python會按except語句的順序依次匹配你指定的異常,如果異常已經處理就不會再進入后面的except語句。
except語句可以以元組形式同時指定多個異常,參見實例代碼。
except語句后面如果不指定異常類型,則默認捕獲所有異常,你可以通過logging或者sys模塊獲取當前異常。
如果要捕獲異常后要重復拋出,請使用raise,后面不要帶任何參數或信息。
不建議捕獲并拋出同一個異常,請考慮重構你的代碼。
不建議在不清楚邏輯的情況下捕獲所有異常,有可能你隱藏了很嚴重的問題。
盡量使用內置的異常處理語句來 替換try/except語句,比如with語句,getattr()方法。
經驗案例
傳遞異常 re-raise Exception捕捉到了異常,但是又想重新引發它(傳遞異常),使用不帶參數的raise語句即可:
在Python2中,為了保持異常的完整信息,那么你捕獲后再次拋出時千萬不能在raise后面加上異常對象,否則你的trace信息就會從此處截斷。以上是最簡單的重新拋出異常的做法。
還有一些技巧可以考慮,比如拋出異常前對異常的信息進行更新。
如果你有興趣了解更多,建議閱讀這篇博客。
http://www.ianbicking.org/blog/2007/09/re-raising-exceptions.html
Python3對重復傳遞異常有所改進,你可以自己嘗試一下,不過建議還是同上。
Exception 和 BaseException
當我們要捕獲一個通用異常時,應該用Exception還是BaseException?我建議你還是看一下 官方文檔說明,這兩個異常到底有啥區別呢? 請看它們之間的繼承關系。
從Exception的層級結構來看,BaseException是最基礎的異常類,Exception繼承了它。BaseException除了包含所有的Exception外還包含了SystemExit,KeyboardInterrupt和GeneratorExit三個異常。
有此看來你的程序在捕獲所有異常時更應該使用Exception而不是BaseException,因為另外三個異常屬于更高級別的異常,合理的做法應該是交給Python的解釋器處理。
except Exception as e和 except Exception, e
代碼示例如下:
在Python2的時代,你可以使用以上兩種寫法中的任意一種。在Python3中你只能使用第一種寫法,第二種寫法被廢棄掉了。第一個種寫法可讀性更好,而且為了程序的兼容性和后期移植的成本,請你也拋棄第二種寫法。
raise “Exception string”
把字符串當成異常拋出看上去是一個非常簡潔的辦法,但其實是一個非常不好的習慣。
上面的語句如果拋出異常,那么會是這樣的:
這在Python2.4以前是可以接受的做法,但是沒有指定異常類型有可能會讓下游沒辦法正確捕獲并處理這個異常,從而導致你的程序掛掉。簡單說,這種寫法是是封建時代的陋習,應該扔了。
使用內置的語法范式代替try/except
Python 本身提供了很多的語法范式簡化了異常的處理,比如for語句就處理的StopIteration異常,讓你很流暢地寫出一個循環。
with語句在打開文件后會自動調用finally中的關閉文件操作。我們在寫Python代碼時應該盡量避免在遇到這種情況時還使用try/except/finally的思維來處理。
再比如,當我們需要訪問一個不確定的屬性時,有可能你會寫出這樣的代碼:
其實你可以使用更簡單的getattr()來達到你的目的。
最佳實踐
最佳實踐不限于編程語言,只是一些規則和填坑后的收獲。
1.只處理你知道的異常,避免捕獲所有異常然后吞掉它們。
2.拋出的異常應該說明原因,有時候你知道異常類型也猜不出所以然的。
3.避免在catch語句塊中干一些沒意義的事情。
4.不要使用異常來控制流程,那樣你的程序會無比難懂和難維護。
5.如果有需要,切記使用finally來釋放資源。
6如果有需要,請不要忘記在處理異常后做清理工作或者回滾操作。
異常速查表
-
編程語言
+關注
關注
10文章
1950瀏覽量
34982 -
異常處理
+關注
關注
0文章
14瀏覽量
7310 -
python
+關注
關注
56文章
4807瀏覽量
85037
原文標題:一文掌握 Python 異常處理的所有知識點
文章出處:【微信號:magedu-Linux,微信公眾號:馬哥Linux運維】歡迎添加關注!文章轉載請注明出處。
發布評論請先 登錄
相關推薦
評論