【Python】try-exceptで例外処理を書く基本

Python

こんにちは、かつコーチです。

「ユーザーが数字以外を入力したらプログラムが落ちてしまう」「ファイルが存在しないだけで処理が止まってしまう」。
こうした問題を防ぐために使うのが例外処理です。

例外処理とは、エラーが発生してもプログラムを止めずに、あらかじめ決めた代わりの処理を実行する仕組みのことです。
この記事では、Pythonのtryexceptelsefinallyの基本構文を、実際に動くコードで解説します。

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を使う

tryexceptに加えて、elsefinallyという2つのブロックも用意されています。

try:
    number = int("100")
except ValueError:
    print("数値に変換できませんでした")
else:
    print(f"変換に成功しました: {number}")
finally:
    print("処理を終了します")
  • elsetryブロックでエラーが発生しなかった場合に実行される
  • finally:エラーの有無に関わらず、必ず最後に実行される

finallyはファイルのクローズ処理や、後片付けの処理を書くときによく使われます。

つまずきやすい設定・注意点

exceptに何も指定しない書き方(bare exceptと呼ばれます)は避けましょう。

# 避けるべき書き方
try:
    result = 10 / 0
except:
    print("エラーが発生しました")

bare exceptは、すべての例外を無条件にキャッチしてしまいます。
本来はプログラムの停止で気づくべき致命的なバグ(例えばKeyboardInterruptSystemExit)まで握りつぶしてしまい、原因究明が困難になります。

必ず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, 初心者向け, エラー解決

タイトルとURLをコピーしました