try-except-else-finally
July 26, 2015
Python は EAFP (Easier to ask for forgiveness than permission) と呼ばれるコーディングスタイルが 推奨されています. 例えば, 次のようなコードを書きたいとしましょう.
>>> scores = {'Alice': [80, 100], 'Bob': [90, 60]}
>>> def math_score(name):
... if name in scores:
... return scores[name][0]
... else:
... print('No data for {}'.format(name))
... return None
もちろんこれはこれで問題ないのですが, math_score
関数は次のように書くこともできます.
>>> def english_score(name):
... try:
... return scores[name][1]
... except KeyError:
... print('No data for {}'.format(name))
... return None
それぞれ, 特定の動作がエラーにならないことを先に確認するコーディングスタイル (LBYL: Look Before You Leap) と, エラーが起きたら対処するというコーディングスタイル (EAFP) です.
どちらが望ましいという訳ではないと思いますが, EAFP スタイルに慣れておきましょう.
極端なことを言えば, Python では例外処理は条件分岐に使われる構文の一種と言えます.
次のコードは, x が 0 でないことをチェックする代わりに,
ゼロ割エラーの発生を検知して条件分岐させます.
def f(x):
try:
y = 1.0 / x
except ZeroDivisionError:
print('an exception raised')
else:
print('no exception')
finally:
print('always printed')
次の str_to_num() 関数は, 文字列を受け取り数値を返します.
整数への変換が失敗した場合に発生する ValueError は関数内部で捕捉され,
浮動小数点数への変換を試みます. さらに, これが失敗した場合は呼び出し元に
ValueError 例外が渡されます.
def str_to_num(s):
try:
return int(s)
except ValueError:
return float(s)
input_number() 関数を実行するとインタプリタは入力待ちになります.
Ctrl+C (強制終了コマンド) を規定の終了動作にするために
KeyboardInterrupt 例外を捕捉しています. (1つ目の try-except)
数字を入力すると, 文字列を数値型に変換しようとします. 変換が失敗すると
ValueError が発生するので, これを補足して入力に誤りがある旨を表示します.
def input_number(msg='Enter a number (^C to cancel)'):
while True:
try:
s = input(msg + ': ')
except KeyboardInterrupt:
print('\nBye!')
break
try:
num = str_to_num(s)
except ValueError:
print('Not a number')
continue
else:
print('Your number is {}'.format(s))
print('Bye!')
break
実行結果:
>>> input_number()
Enter a number (^C to cancel): 1
Your number is 1
Bye!
>>> input_number()
Enter a number (^C to cancel): -1
Your number is -1
Bye!
>>> input_number()
Enter a number (^C to cancel): 1.23
Your number is 1.23
Bye!
>>> input_number()
Enter a number (^C to cancel): 1e-10
Your number is 1e-10
Bye!
>>> input_number()
Enter a number (^C to cancel): 1.2.3
Not a number
Enter a number (^C to cancel): abc
Not a number
Enter a number (^C to cancel): ^C
Bye!
最後のコードは, Python スクリプト実行時のオプション有無で振る舞いを変わります.
実行時のオプションは sys.argv によって取得できます.
sys.argv は長さが1以上のリストで, オプションなしで実行された場合には,
実行されたスクリプトのファイル名のみを格納した長さが1のリストになります.
オプションが渡された場合には sys.argv[1], sys.arg[2] .... でオプションを文字列として取得できます.
len(sys.argv) > 1 の真偽でオプション有無を判定してもよいのですが,
次のようにしても同じことです.
# hello.py
import sys
try:
name = sys.argv[1]
except IndexError:
print('Hello, world!')
else:
print('Hello, {}'.format(name))
finally:
print('Bye')
シェルで実行してみます.
$ python hello.py
Hello, world!
Bye
$ python hello.py Kenji
Hello, Kenji
Bye
Comments