try-exceptで備える
このレッスンで作るもの — 変換に失敗しても止まらない
safe_intを作り、おかしな入力でも落ちないおこづかい帳にします。
try-except で備える
第 2 章で int("1200") を学びました。文字列を数値に変えるあの関数です。ただし、変換できる文字列にしか使えません。
int("120") # 120 になる
int("abc") # ValueError で止まるint("abc") を実行すると、ValueError という例外が起きてプログラムがそこで止まります。メッセージは invalid literal for int with base 10 と出ます。「10 進数として読めない文字だ」という意味です。
おこづかい帳では、金額の欄に打ち間違いが入ることがあります。全角で 120 と打ってしまう、うっかり 120円 と単位まで打ってしまう、空欄のまま送ってしまう。どれも int では読めません。
入力を扱うプログラムは、正しい入力より 正しくない入力のほうが多い くらいの気持ちで書きます。落ちないことが最低条件です。
例外は止めるのではなく受け止める
例外が起きても止まらないようにする構文が try と except です。
try:
number = int(value)
except ValueError:
number = 0try の中に「失敗するかもしれない処理」を書きます。もし例外が起きたら、その時点で try のブロックを抜け、except のブロックに移ります。例外が起きなければ except は素通りされます。
上のコードは「変換できたらその数値、できなかったら 0」という意味になります。except ValueError と種類を書いているので、受け止めるのは ValueError だけです。
try の中に return を書いてしまっても構いません。むしろそのほうが短く書けます。
def safe_int(value):
try:
return int(value)
except ValueError:
return 0変換が成功すれば 1 つ目の return で関数が終わります。失敗したときだけ except に落ちて 0 を返します。
int が通るもの、通らないもの
int は意外と寛容な部分もあります。前後の空白は無視してくれるので、int(" 80 ") は 80 になります。マイナス記号も読めるので int("-50") は -50 です。
一方で、小数点の入った "12.5" は ValueError になります。文字列からの変換では小数を経由してくれません。空文字 "" も読めません。
| 渡す値 | 結果 |
|---|---|
"120" | 120 |
" 80 " | 80 |
"abc" | ValueError |
"" | ValueError |
"12.5" | ValueError |
すでに int である値を渡した場合は、そのまま同じ数値が返ります。int(250) は 250 です。無駄にはなりますが害もないので、判定を書かずに全部 safe_int に通してしまうのが楽です。
どんな値が来ても数値が返る関数を 1 つ用意しておくと、呼び出す側から場合分けが消えます。安全な入口を 1 か所にまとめる、という考え方です。
よくある間違い
exceptの範囲を広げすぎる —except:とだけ書くと、あらゆる例外を飲み込んでしまいます。本当のバグまで隠れて原因が追えなくなるので、受け止める種類は必ず書きます。tryの範囲を広げすぎる —tryのブロックに関係ない処理まで入れると、どこで失敗したのか分からなくなります。危ない 1 行だけを囲むのが基本です。exceptで何もしない —except ValueError: passと書くと、失敗を無視して先に進みます。今回は0を返すという後始末があるので、passは使いません。
if で事前に判定しようとして、条件が延々と増えていくのもよくある回り道です。「やってみて失敗したら対処する」ほうが Python では素直です。
やってみよう
関数 safe_int(value) を実装してください。value を int に変換して返します。変換できないときは ValueError を受け止めて 0 を返します。
try の中に return int(value) を書き、except ValueError の中に return 0 を書く形が最短です。"120" は 120、"abc" は 0、空文字 "" も 0 になれば正解です。すでに数値の 250 を渡しても 250 が返ることを確かめてみてください。
要件
- 関数名は safe_int で、引数は value の 1 つ
- try の中で int による変換を試す
- except ValueError で受け止め、0 を返す
- except の種類を省略して全部を飲み込む書き方はしない
- 変換に成功したときはその数値をそのまま返す
入出力例
safe_int("120") → 120
safe_int("abc") → 0
safe_int("") → 0
safe_int("0") → 0
safe_int(" 80 ") → 80
safe_int(250) → 250
safe_int("12.5") → 0