例外與捕捉
在動態語言中,除了語法上的錯誤造成的 Syntax Error
,幾乎所有的錯誤都來自執行期的錯誤,如同前面見過的TypeError
或是 NameError
等都是。
當發生錯誤時,Python會產生 回溯(traceback),這讓我們可以檢視錯誤發生的來源,有的時候一個錯誤是另外一個錯誤所引發的,唯有一層一層的檢視,才能找出錯誤。
不過,在本節中要討論的不是除錯,而是辨識並排除掉一些不是錯誤的錯誤,因為錯誤並不全然都是錯誤,這聽起來有點玄,讓我們舉個讀取檔案的例子,首先我們來看看要讀的檔 test_file
:
3 4
1 3
20 19
%%%
88 7
1 2 3
0
來看看我們的程式碼:
with open('test_file') as f:
for line in f:
a, b = line.strip().split()
print(int(a)+int(b))
我們先對第一次出現的方法 split
,作解說,split
是字串的方法,他能夠將字串依照指定的分隔符號切割。
舉例來說:
- 字串
string='hello,world'
可以利用string.split(',')
來切割成'hello'
和'world'
- 字串
string='hello-world'
可以利用string.split('-')
來切割成'hello'
和'world'
如果我們不給定參數,則預設會用空白分割,比如說:
- 字串
string='hello world'
可以利用string.split()
來切割成'hello'
和'world'
。
因此這個程式首先打開了我們的檔案 f
,接著利用 for
迴圈去取出每一行字串,取出的字串先用 strip
方法作一個清理,接著使用 split
方法利用空白符號作切割,我們切割成兩個字串 a
和 b
(a, b = line.strip().split()
這種寫法就是 unpack,我們會將拆解完的結果依序賦值給 a
, b
),最後將兩個字串轉成整數後相加。
但我們發現執行的時候出現了 ValueError
,這很正常,因為檔案中有許多行並不符合兩個整數的格式,有的行是空白行,有的行有三個整數或只有一個整數,還有的行是奇怪的符號。
如果我們要利用 if/else 的手法來排掉這些 "例外" 的狀況,會顯地非常複雜,每一種可能有誤的格式都需要寫一個 if
或 elif
來避開,我們與其花大量的時間來避開錯誤,不如讓錯誤發生,我們再來處理,讓會中斷程式的錯誤僅僅是允許繼續執行的例外:
with open('test_file') as f:
for line in f:
try:
a, b = line.strip().split()
print(int(a)+int(b))
except:
pass
例外的捕捉,用的語法是 try
跟 except
,我們先 try
一段程式碼,如果發生了錯誤,我們立刻停止 try
,進入 except suite 做例外發生時的處置。
所以當我讀到非兩個整數的行時會導致錯誤發生,但因為我們只是 try
,我們當場捕捉到了這個錯誤 (例外),於是我們就不往下執行 print
,而是直接跳至 except suite 去執行,但因為這邊是個空運算,所以等於是我們略過了所有不符合規格的行。
這裡我們看到了空運算的用途,Python 不允許任何一個 suite 是空白的,所以當我們什麼都不想處理時,必須補上一個
pass
讀者越熟悉例外,越會覺得他威力無窮,很多很複雜、很瑣碎很難想清楚的狀況,與其利用 if/elif/else 針對每種狀況寫出處理手段以避開錯誤,不如利用 try
,大膽執行程式碼,等到錯誤發生時,將之捕捉當成例外。
這種手法往往可以讓我們開發的更快,錯誤更少。畢竟,錯誤不一定是錯誤,他可能只是個例外。