使用 else、finally
May 2, 2022try
、except
的語法,還可以搭配 else
、finally
來使用,當 else
區塊出現時,若 try
區塊沒有發生例外,else
就會執行,如果 finally
區塊出現時,無論 try
區塊中有沒有發生例外,finally
區塊都一定會執行。
try/except/else
else
可與 try
、except
搭配,是其他語言中不常見的,else
可與 try
、except
搭配的原因在於,讓 try
的程式碼,盡量與可能引發例外的來源相關。例如:
numbers = input('輸入數字(空白區隔):').split(' ')
try:
ints = [int(number) for number in numbers]
except ValueError as err:
print(err)
else:
print('平均', sum(ints) / len(ints))
在這個範例中,try
區塊集中在嘗試執行 int
,緊接著的 except
用以比對 ValueError
,這樣程式碼上就可以清楚地看出,int
與 ValueError
的關係,若沒有引有例外,就會執行 else
區塊以顯示結果。
在 Python 官方文件〈Errors and Exceptions〉也有個範例如下:
for arg in sys.argv[1:]:
try:
f = open(arg, 'r')
except OSError:
print('cannot open', arg)
else:
print(arg, 'has', len(f.readlines()), 'lines')
f.close()
在上面的範例中,open
呼叫時若沒有因檔案開啟失敗而引發例外,就會執行 else
區塊的內容,這會比撰寫以下的程式來得好:
for arg in sys.argv[1:]:
try:
f = open(arg, 'r')
print(arg, 'has', len(f.readlines()), 'lines')
f.close()
except OSError:
print('cannot open', arg)
在上面的程式中,如果真的引發了例外,那到底是 open
引發的例外,還是 readlines
引發的例外呢?如果是 readlines
引發的例外,那麼 except
中 'cannot open'
的訊息顯示,可能就誤導了除錯的方向。
因為 try
區塊沒有發生例外,else
就會執行,在測試的場合,也會看到 try/except/else
的搭配,例如:
try:
do_some() # 預期會拋出例外
except SomeError:
pass
else:
fail('測試失敗的相關訊息')
try/finally
在 Python 官方文件〈Errors and Exceptions〉的範例中,實際上 readlines
也是有可能引發例外,如果檔案順利開啟,然而 readlines
引發了例外,那麼最後的 f.close
就不會被執行,如果想確保 f.close
一定會執行,可以修改如下:
import sys
for arg in sys.argv[1:]:
try:
f = open(arg, 'r')
except FileNotFoundError:
print('找不到檔案', arg)
else:
try:
print(arg, ' 有 ', len(f.readlines()), ' 行 ')
finally:
f.close()
由於 finally
區塊一定會被執行,這個範例要關閉檔案的動作,一定得是在檔案開啟成功,而 f
被指定了檔案物件之後,如果這麼撰寫:
import sys
for arg in sys.argv[1:]:
try:
f = open(arg, 'r')
except FileNotFoundError:
print('找不到檔案', arg)
else:
print(arg, ' 有 ', len(f.readlines()), ' 行 ')
finally:
f.close()
那麼若檔案開啟失敗,就不會建立 f
變數,最後執行 finally
的 f.close
時,就會引發 NameError
並且指出f名稱未定義。
如果程式撰寫的流程中先 return
了,而且也有寫 finally
區塊,那 finally
區塊會先執行完後,再將值傳回。例如,下面這個範例會先顯示「finally」再顯示「1」:
def test(flag: bool):
try:
if flag:
return 1
finally:
print('finally')
return 0
print(test(True))