給妹子講python-S01E24深入解析異常處理方式

醬油哥在掘金發表於2018-08-22

歡迎關注公眾號:python資料科學家

【要點搶先看】

1.try、except、else、finally、raise關鍵字
2.異常的處理流程
3.try/except/else/finally組成的幾種異常處理模式

通過上一節我們知道了python異常的整個框架。本節會詳細介紹異常編碼的語法模式,try/except/else和try/finally。

先重新回顧一下try、except、else、finally幾個關鍵字:

try後面緊跟著縮排的語句程式碼,代表此語句的主要動作:試著執行的程式程式碼。

然後是一個或多個except分句來識別要捕獲的異常,except子句內定義try程式碼塊內引發的異常處理器,

最後是一個可選的else分句,提供沒發生異常時要執行的語句。

分別討論下面的幾種情形:

如果try程式碼塊語句執行時的確發生了異常,python就跳出try,執行第一個符合引發異常的except子句下面的語句。當except程式碼塊執行結束後,控制權就會到整個try程式碼塊後繼續執行。

如果異常發生在try程式碼塊內,沒有符合的except子句,異常就會傳遞到頂層,迫使python終止這個程式並列印預設的出錯資訊。

如果try首行底下執行的語句沒有發生異常,python就會執行else行下的語句,控制權會在整個try語句下繼續。

換句話說,except分句會捕獲try程式碼塊執行時所發生的異常,而else子句只在try程式碼塊執行時不發生異常才會執行。

except是專注於異常處理器的:捕捉只在相關try程式碼塊中的語句所發生的異常。儘管這樣,因為try程式碼塊語句可以呼叫寫在程式其他地方的函式,異常的來源可能在try語句自身之外。

關於except子句的一些說明:

except子句可以用括號列出一組異常[except (e1,e2,e3)],而如果except子句後沒有列出異常名稱,即except:時,會捕捉所有的異常型別。

但是,空except也會引發一些設計的問題,儘管方便,也可能捕捉和程式程式碼無關、意料之外的系統異常,而且可能意外攔截其他處理器的異常。例如,在python中,即使是系統離開呼叫,也會觸發異常,而顯然你通常會想讓這些事件通過。

python引入了一個替代方案來解決這個問題,捕獲一個名為Exception的異常,幾乎與一個空的except:具有相同的效果,但是忽略和系統退出相關的異常。

來看看try/else語句的作用

也許我們無法一眼看出else子句的用途,不過仔細想想,如果沒有else,是無法知道控制流程是否通過了try語句,到底是沒有異常引發,還是異常發生了且已被處理過了,不使用else的話很難分得清。

再來分析一下try/finally語句

try中包含了finally子句,python一定會在try語句後執行其語句程式碼塊,無論try程式碼塊執行時是否發生異常。

利用這個變體,python可先執行try首行下的語句程式碼塊。接下來發生的事情,取決於程式碼塊中是否發生異常:

如果try程式碼塊執行時沒有異常發生,python會跳至執行finally程式碼塊,然後在整個try語句後繼續執行下去。

如果try程式碼塊執行時有異常發生,python依然會回來執行finally程式碼塊,但是接著會把異常向上傳遞到較高的try語句或頂層的預設異常處理器,程式不會在try語句下繼續執行。也就是說,即使發生了異常,finally程式碼塊還是會執行的,和except不同的是,finally不會終止異常,而是在finally程式碼塊執行後,丟擲異常。

當想確定某些程式程式碼執行後,無論程式的異常行為如何,有個動作一定會發生,那麼,try/finally形式就很有用。在實際應用中,這可以讓你定義一定會發生的清理動作,最直觀的就是,在出現異常時,仍能利用finally關閉檔案和斷開伺服器連線。

最後我們來看最完整的形式:try/except/else/finally

try:
    main-action
except Exception1:
    handler1
except Exception2:
    handler2
...
else:
    else-block
finally:
    finally-block
複製程式碼

我們從頭梳理一遍:

就像往常一樣,這個語句中的main-action程式碼會先執行。如果該程式程式碼引發異常,那麼所有except程式碼塊就會逐一測試,尋找與丟擲的異常相符的語句,如果引發的異常是Exception1,就會執行handler1,如果引發的的異常是Exception2,就會執行handler2,以此類推,如果沒有引發任何異常,將會執行else-block。而無論之前發生了什麼,當main-action程式碼塊完成的時候,而任何引發的異常都已經處理後,finally-block就會執行。事實上,即使異常處理器或者else-block內有錯誤發生而引發新的異常,finally-block內的程式程式碼依然會執行。就像之前所說的那樣,finally子句並沒有終止異常:當finally-block執行的時候,如果異常還存在,就會在finally-block程式碼塊執行後繼續傳遞,而控制權會跳至程式其他地方,如我們的預設的頂層處理器。

最後我們叮囑一下,try語句必須有一個except或一個finally,else是可選的,但是如果有else,則必須至少有一個except。

最後我們簡單的再說說raise

要顯式的觸發異常,可使用raise語句,其一般形式相當簡單。raise語句的組成是:raise+可選的要引發的類或者類的一個例項。

如果傳遞的是一個類,其實python會呼叫不帶建構函式引數的類,以建立被引發的一個例項;這個格式等同於在類後面新增圓括號。本質上仍然是傳遞了一個物件。

我們看一個例子:對於之前我們經常看到的IndexError,下面兩種形式是對等的,

raise IndexError
raise IndexError()
複製程式碼

都會引發指定異常類的一個例項,其中第一種形式隱式的建立例項。

當使用下列的語法形式的時候

try:
    pass
except IndexError as e:
    print(e.args)
複製程式碼

實質是把捕捉到的異常物件賦予了e這個變數名,這樣可以方便的訪問異常例項的資料以及異常類中的方法。

公眾號二維碼:python資料科學家:

給妹子講python-S01E24深入解析異常處理方式

相關文章