前面我們講到Python程式設計過程中,在可能出現異常的地方使用try語句,來正確的處理一些異常,可以保證程式不中斷繼續執行。
丟擲異常
有時候,我們可能需要在程式的某些地方主動丟擲異常,通知呼叫該程式碼的程式有錯誤發生。這時候,我們就要用到raise
語句。raise
語句就是幫助我們丟擲知道異常的,比如:
In [6]: raise NameError("Bad Name")
-----------------------------------
NameError Traceback (most recent call last)
<ipython-input-6-966a00c8f456> in <module>
----> 1 raise NameError("Bad Name")
NameError: Bad Name
raise
的使用很簡單,它的語法如下:
raise [expression [from expression]]
如果它後面不帶表示式(引數),它會重新引發當前作用域內最後一個啟用的異常。 如果當前作用域內沒有啟用的異常,將會引發 RuntimeError 來提示錯誤。
如果後面帶有表示式,則將表示式求值為要丟擲的異常物件,該表示式必須是一個異常例項或者是一個異常類(繼承自BaseException
類)。如果它是一個異常類,它將透過呼叫沒有引數的建構函式來隱式例項化:
raise NameError # 等同於 'raise NameError()'
raise
表示式後面還可以跟一個from
子句,用於異常的串聯。from
子句的表示式必須是另一個異常或例項,它將作為可寫的(writable)的__cause__
屬性被關聯到所引發的異常。如果引發的異常未被捕捉處理,兩個異常都將被列印出來:
In [9]: try:
...: print(10/0)
...: except Exception as e:
...: raise RuntimeError("something is wrong") from e
...:
----------------------------------------------------------
ZeroDivisionError Traceback (most recent call last)
<ipython-input-9-7de64aad634f> in <module>
1 try:
----> 2 print(10/0)
3 except Exception as e:
ZeroDivisionError: division by zero
The above exception was the direct cause of the following exception:
RuntimeError Traceback (most recent call last)
<ipython-input-9-7de64aad634f> in <module>
2 print(10/0)
3 except Exception as e:
----> 4 raise RuntimeError("something is wrong") from e
5
RuntimeError: something is wrong
如果一個異常在except
子句或finally
子句中被丟擲,類似的機制會隱式地發揮作用,之前的異常將被關聯到新異常的__context__
屬性。例如:
In [10]: try:
...: print(10/0)
...: except:
...: raise RuntimeError("something is wrong")
...:
-----------------------------------------------
ZeroDivisionError Traceback (most recent call last)
<ipython-input-10-e950a6292482> in <module>
1 try:
----> 2 print(10/0)
3 except:
ZeroDivisionError: division by zero
During handling of the above exception, another exception occurred:
RuntimeError Traceback (most recent call last)
<ipython-input-10-e950a6292482> in <module>
2 print(10/0)
3 except:
----> 4 raise RuntimeError("something is wrong")
5
RuntimeError: something is wrong
異常串連可透過在from
子句中用None
來顯示地禁止:
In [11]: try:
...: print(10/0)
...: except:
...: raise RuntimeError("something is wrong") from None
...:
-------------------------------
RuntimeError Traceback (most recent call last)
<ipython-input-11-1818bd8b9d31> in <module>
2 print(10/0)
3 except:
----> 4 raise RuntimeError("something is wrong") from None
5
RuntimeError: something is wrong
使用者自定義異常
Python允許使用者自定義異常類,通常應該直接或間接地繼承自Exception
類。
自定義的異常類的名稱通常以“Error”結尾,類似與內建標準異常的命名。自定義的異常類,可以像其它類那樣可以執行任何操作,但通常保持簡單,只提供用以處理程式為異常提取有關錯誤資訊的屬性。為模組自定義多個不同錯誤的異常時,通常是為該模組定義一個異常基類,再為不同錯誤建立特定的子類。例如:
class ModuleError(Exception):
'''模組的異常基類'''
pass
class ModuleNameError(ModuleError):
'''模組的特定異常子類'''
pass
class ModuleValueError(ModuleError):
'''模組的另一個特定異常子類'''
pass
最後的清理操作:finally 子句
finally
子句是try
語句的一個可選子句,用於定義在任何情況下都執行的操作,叫做“清理操作”。例如:
In [12]: try:
...: raise NameError
...: finally:
...: print('Bye :)')
...:
...:
Bye :)
-------------------------------
NameError Traceback (most recent call last)
<ipython-input-12-9cda1523ce81> in <module>
1 try:
----> 2 raise NameError
3 finally:
4 print('Bye :)')
5
NameError:
finally
子句總會在離開try
語句前被執行,無論發生異常與否。當在try
子句中發生了異常且尚未被except
子句處理(或者它發生在 except 或 else 子句中)時,該異常將在 finally 子句執行後被重新丟擲。 當 try 語句的任何其他子句透過 break, continue 或 return 語句離開時,finally 也會在“離開之前”被執行,參考下面這個更復雜的例子:
In [13]: def divide(a, b):
...: try:
...: result = a / b
...: except ZeroDivisionError:
...: print('divided by zero!')
...: else:
...: print('result is', result)
...: finally:
...: print('leaving try')
...:
In [14]: divide(8, 2)
result is 4.0
leaving try
In [15]: divide(8, 0)
divided by zero!
leaving try
In [16]: divide('a', 2)
leaving try
-----------------------
TypeError Traceback (most recent call last)
<ipython-input-16-324d9fa22da2> in <module>
----> 1 divide('a', 2)
<ipython-input-13-5e4380c62566> in divide(a, b)
1 def divide(a, b):
2 try:
----> 3 result = a / b
4 except ZeroDivisionError:
5 print('divided by zero!')
TypeError: unsupported operand type(s) for /: 'str' and 'int'
從上面的例子我們看到,finally
子句總是會被執行。但字串被除時引發了TypeError
的異常,這個異常沒有被except
子句處理,就會在finally
子句執行後被重新丟擲。
在程式設計實踐中,finally
子句對釋放檔案或網路連線等外部資源是非常有用的。
總結
程式設計中,我們不僅要在恰當的地方處理異常,也要在必要的時候丟擲異常,我們丟擲異常時可以自定義異常。熟練運用異常可以使我們的程式更加健壯,別忘了必要的時候使用finally
來釋放外部資源。
我的公眾號:猿人學 Python 上會分享更多心得體會,敬請關注。
***版權申明:若沒有特殊說明,文章皆是猿人學 yuanrenxue.com 原創,沒有猿人學授權,請勿以任何形式轉載。***