こんにちは、かつコーチです。
「ユーザーが数字以外を入力したらプログラムが落ちてしまう」「ファイルが存在しないだけで処理が止まってしまう」。
こうした問題を防ぐために使うのが例外処理です。
例外処理とは、エラーが発生してもプログラムを止めずに、あらかじめ決めた代わりの処理を実行する仕組みのことです。
この記事では、Pythonのtry・except・else・finallyの基本構文を、実際に動くコードで解説します。
try-exceptの基本の書き方
手順1:もっとも基本的なtry-except
まずは基本形から見ていきましょう。
try:
number = int("abc")
except ValueError:
print("数値に変換できませんでした")
tryブロックの中でエラーが起きそうな処理を実行します。
エラーが発生すると、そこで処理が中断され、対応するexceptブロックが実行されます。
このコードでは、文字列"abc"をint()で数値に変換しようとしてエラーになりますが、プログラム自体は止まらずにexcept内の処理が実行されます。
手順2:複数の例外を使い分ける
エラーの種類ごとに、別々の対応をしたい場合もあります。
data = {"price": 1000}
try:
value = data["quantity"]
result = 100 / value
except KeyError:
print("キーが存在しません")
except ZeroDivisionError:
print("0で割ることはできません")
exceptは複数並べて書くことができ、上から順にエラーの型と一致するかどうかがチェックされます。
このコード例ではdataに"quantity"キーが存在しないため、KeyError用のexceptが実行されます。
手順3:elseとfinallyを使う
try・exceptに加えて、elseとfinallyという2つのブロックも用意されています。
try:
number = int("100")
except ValueError:
print("数値に変換できませんでした")
else:
print(f"変換に成功しました: {number}")
finally:
print("処理を終了します")
else:tryブロックでエラーが発生しなかった場合に実行されるfinally:エラーの有無に関わらず、必ず最後に実行される
finallyはファイルのクローズ処理や、後片付けの処理を書くときによく使われます。
つまずきやすい設定・注意点
exceptに何も指定しない書き方(bare exceptと呼ばれます)は避けましょう。
# 避けるべき書き方
try:
result = 10 / 0
except:
print("エラーが発生しました")
bare exceptは、すべての例外を無条件にキャッチしてしまいます。
本来はプログラムの停止で気づくべき致命的なバグ(例えばKeyboardInterruptやSystemExit)まで握りつぶしてしまい、原因究明が困難になります。
必ずexcept ValueError:のように、捕まえたいエラーの種類を明示するようにしましょう。
どうしても種類を特定できない場合は、少なくともexcept Exception as e:として、変数eにエラー内容を格納しprint(e)で確認できるようにします。
よくあるつまずきポイント・エラー対処
エラーの種類を指定せずに原因が分からなくなる
Before(つまずいたコード)
try:
with open("data.csv") as f:
content = f.read()
numbers = [int(x) for x in content.split(",")]
except:
print("エラーが発生しました")
私は業務の自動化スクリプトで、このコードを書いて実際にハマったことがあります。
「エラーが発生しました」とだけ表示され、ファイルが見つからないのか、数値への変換に失敗しているのか、原因が全く分かりませんでした。
結局、exceptの中身を1つずつコメントアウトしながら原因を探るという、非効率な作業をする羽目になりました。
After(改善したコード)
try:
with open("data.csv") as f:
content = f.read()
numbers = [int(x) for x in content.split(",")]
except FileNotFoundError:
print("data.csvが見つかりません")
except ValueError as e:
print(f"数値への変換に失敗しました: {e}")
エラーの種類ごとにexceptを分け、ValueError側ではas eでエラー内容を変数に格納して表示するようにしました。
これにより、どちらの原因でエラーになったのかが一目で分かるようになります。
例外を握りつぶして不具合に気づけない
Before(つまずいたコード)
def get_user_age(user_data):
try:
return int(user_data["age"])
except:
return 0
このコードは一見動きますが、user_dataに"age"キー自体が存在しない場合(KeyError)も、年齢が"abc"のような不正な値の場合(ValueError)も、区別なく0が返ってしまいます。
本来はデータ不備として検知すべき問題が、静かに「年齢0歳」というおかしなデータになって処理が進んでしまいます。
私はこれが原因で、後工程の集計処理の結果がおかしくなり、原因調査に半日かかったことがあります。
After(改善したコード)
def get_user_age(user_data):
try:
return int(user_data["age"])
except (KeyError, ValueError) as e:
print(f"年齢データの取得に失敗しました: {e}")
return None
捕まえたい例外の種類を(KeyError, ValueError)のようにタプルで明示し、失敗した場合は0ではなくNoneを返すようにしました。Noneを返すことで「値が取得できなかった」ことが呼び出し元にも伝わり、意図しないデータの誤りに気づきやすくなります。
まとめ
この記事のポイント
tryブロックでエラーが起きそうな処理を囲み、exceptで対応する処理を書きますexceptはエラーの種類ごとに複数並べることができますelseはエラーが起きなかった場合、finallyは必ず実行される後処理に使いますexcept:のようなbare exceptは避け、エラーの種類を明示しましょう- 例外を握りつぶすと不具合に気づけなくなるため、
as eで内容を確認できるようにしましょう
次に読むべき記事
エラーメッセージの読み方に自信がない方は、よくあるPythonエラーの読み方の記事もあわせてご覧ください。
Noneの扱いでつまずきやすい方は、Noneの扱い方の記事も参考になります。
タグ: Python, 初心者向け, エラー解決