左手用R右手Python系列——異常捕獲與容錯處理
一套穩健的代碼體系,必須能夠包容所有可能出現的錯誤情況並做出針對性處理,要想達到這個目標,務必要對異常捕獲與容錯處理有深入的了解和認識。
秉著初學者入門探索的心態,接下來的兩篇我會陸續跟大家分享R語言與Python中所涉及到的主要異常捕獲與容錯處理機制。今天先分享異常及其捕獲手段,下一篇會綜合實戰應用講解如何在循環中繞過異常或者跳出循環。
R語言中的異常函數主要涉及兩個:
- tryCatch:
- try:
tryCatch函數擁有類似Python中的try/expect那樣相對完整的容錯處理機制。一個完整的tryCatch容錯函數,一般具有以下結構:
result <- tryCatch({n ###等待排錯的語句n expr n}, warning = function(w) {n ###捕獲警告(警告僅僅善意提醒,不會導致程序中斷,屬於非致命異常,通常以warning開頭)n warning-handler-code n}, error = function(e) { n ###捕獲錯誤(錯誤是比較嚴重的故障,倘若不捕獲並處理,n ###則會通過編輯器拋出錯誤信息並中斷程序運行,因而屬於致命異常,是我們重點處理對象)n error-handler-coden}, finally = {n ###finally屬於無論錯誤與否都會執行的必須語句,這一點與Python中的try/expect中的finally語句用法相同n cleanup-coden})n
- right:http://raindu.com/
- no: http://raindu.edu/
以上是我的個人博客地址,其中第一個網址是正確的,第二個是錯誤的(不存在的)。我們將這兩個網址封裝在一個向量里。
library("RCurl")nurl<-c(n "http://raindu.com/",n "http://raindu.edu/"n)n
接下來使用getURL來進行網頁請求。
getURL(url[1])n
當你請求了正確的網址時,總是可以從輸出中得到想要的內容。
getURL(url[2]) n
倘若不幸請求了不存在的網址(或者域名),編輯器直接拋出錯誤並中斷程序(如果你沒有針對異常進行處理的話)。
如果我們想要包裝一下此異常,指定一個規則,如果網址存在則返回1,否則返回0,那麼這兩個條件要求我們必須明確的判斷兩次請求的狀態。可以嘗試著使用tryCatch函數來進行封裝並捕獲可能出現的異常。
result<-tryCatch({n getURL(url[1]) n}, error = function(e) { n print(e)n}, finally = {n print("程序運行完了!!!")n}) n
因為url[1]是正確的網址,所以以上代碼運行之後,getURL(url[1])的得到的網頁請求結果被保存在了result對象中,沒有異常出現,所以也就沒有列印異常信息,finally是無論出錯與否都會執行的語句,所以finally語句也執行了。
再次列印result可以看到請求的網頁內容。
那麼換做url[2]的情況如何呢?
result<-tryCatch({n getURL(url[2]) n}, error = function(e) { n print(e)n}, finally = {n print("程序運行完了!!!")n}) n
以上異常信息是我們截獲到的,而非系統拋出的,從結果我們可以很清晰的看到tryCatch的異常處理邏輯:即倘若首段代碼無異常,則正常運行並返回網頁內容,如果出錯,則首段代碼放棄執行,進入異常模塊(error)執行模塊內語句,執行完畢之後則繼續執行finally語句。
那麼通常將兩次任務結合結合起來,正確執行返回1,否則返回0。
for (i in url){n tryCatch({n result<-getURL(i)n print(1) n}, error = function(e) { n print(0)n}, finally = {n print("程序運行完了!!!")n})n} n
因為url中僅有兩個網址,所以循環僅僅執行了兩次,第一次返回1,說明請求成功了,tryCatch中的第一個模塊語句所有語句都被執行了,第二次語句出現了錯誤,則函數直接切換到error模塊,執行print(0),無論成功與否,兩次程序都執行了finally中的print(「程序運行完了!!!」)語句。以上便是tryCatch中的異常鋪貨邏輯,只要你在程序中設置了正確的異常捕獲機制,那麼異常變回按照你所自定義的任務進行執行,否則異常會通過編輯器的錯誤信息彈出,並強制中斷程序。
try函數的邏輯更為簡單粗暴,它只是一個錯誤與否的判定器(理解粗淺不要見笑),我們需要根據try的結果中是否包含錯誤來進行邏輯判斷,進而執行後續操作,相當於我們要人為構建error模塊中的任務執行措施。
for (i in url){nError <- try(getURL(i))n if(!try-error %in% class(Error)){n print("請求成功,請求狀態為:1")n } else {n print("請求失敗,請求狀態為:0")n }n} n
讓我們再來對比一下tryCatch與try兩種異常捕獲機制的差別,tryCatch的tryCatch/warning/error/finally機制是無需定義的封裝一體化的容錯處理機制,而try的異常捕獲機制則是我們通過if判斷try語句的結果中是否包含錯誤類型,通過if/else來進行判斷病處理的,所以很明顯,tryCatch機制更加健壯,可以告知我們更多錯誤信息,設置更多後續處理時間,並且無需自定義關鍵詞。而try則相對羸弱一些,我們需要自己造輪子。
所以說如果你不想具體糾纏於錯誤類型和內部機制,使用try會更簡潔一些,但是需要自己做if判斷是遇到錯誤跳出還是繞過(至於如何在循環中跳出錯誤或者繞過錯誤,敬請收看下文),而tryCatch則具有更加完善的捕獲與處理機制,更加智能一些。
Python
Python的中錯誤處理僅以try/except/else/finally為例進行簡單梳理(至於更為高階的異常捕獲與容錯處理,可以參見官方文檔)。
- try
- except
- else
- finally
Python中的異常捕獲機制的完整流程如上所列。try的含義與R語言中的tryCatch第一個子模塊一樣,是等待排錯的代碼段。
except則與R中的error模塊異曲同工。倘若try模塊語句出錯,則錯誤代碼塊停止執行,直接切入except模塊執行異常處理。else倘若try模塊無異常,則程序執行完try模塊之後就會直接跳到else模塊執行該模塊語句,否則執行執行expect模塊。finally模塊則與R語言中的finally語句一樣,無論是否出錯,最後都會執行(只要勇於文件讀寫)。這樣對標起來,其實Python中的try系統也是很好理解的。
R Python ntryCatch trynerror exceptnfinally finallyn
省略Python中的else模塊(感覺這個模塊用的很少),可以看到R語言與Python中的異常捕獲邏輯是想通的。
但是Python的try模塊中,對於except模塊的錯誤類型非常講究,一個try系統可以容納多個子異常(except語句),異常語句中可以不指定異常類型(捕獲所有異常),也可以指定異常類型(原則是多條子異常,子類在前,父類在後)。
http://www.cnblogs.com/rubylouvre/archive/2011/06/22/2086644.html
這篇文章對異常的樹結構進行了很好地梳理,可以參考。本著簡單實用的原則,本篇文章不對異常的細節做過多探討。
from urllib.request import Request,urlopennfrom urllib import errornurl=["http://raindu.com/","http://raindu.edu/"]ni=0nwhile(i<2):n try:n response = urlopen(Request(url[i])) n print("請求正確,狀態碼為1")n except error.URLError as e:n print("請求錯誤,狀態碼為0,錯誤信息為",e)n i+=1n n
當然也可以用for循環來寫,畢竟for和while是可以相互替代的操作。
for i in url:n try:n response = urlopen(Request(i)) n print("請求正確,狀態碼為1")n except error.URLError as e:n print("請求錯誤,狀態碼為0,錯誤信息為",e)n n
事實上,except模塊的錯誤信息並不是必須的,你甚至可以省略掉錯誤信息,這樣仍然可以自定義出錯狀態下應該執行的操作,只是無法獲知詳細的錯誤信息而已。
for i in url:n try:n response = urlopen(Request(i)) n print("請求正確,狀態碼為1")n except error.URLError:n print("請求錯誤,狀態碼為0")nnfor i in url:n try:n response = urlopen(Request(i)) n print("請求正確,狀態碼為1")n except:n print("請求錯誤,狀態碼為0")n
以上便是Python中的異常捕獲機制,想要了解詳細的except模塊使用技巧,以及諸多錯誤類型的內含和差異,還需要進一步參考官文檔。
https://docs.python.org/3/tutorial/errors.html
至於是實際應用場合,如何在循環中繞過錯誤記錄,跳出指定錯誤,下篇文章會使用真實案例進行情景介紹,敬請期待!
在線課程請點擊文末原文鏈接:
Hellobi Live | 9月12日 R語言可視化在商務場景中的應用
往期案例數據請移步本人GitHub:https://github.com/ljtyduyu/DataWarehouse/tree/master/File
推薦閱讀: