基礎から学ぶPython入門 90日コース | Python基礎 - Day 11:例外処理

Python 90日で身につけるPython
スポンサーリンク
スポンサーリンク

Day 11:例外処理の考え方と書き方

Day 11では、「例外処理」をテーマに、Pythonのエラーの種類と、tryexceptelsefinallyraise を使った安全なコードの書き方を学んでいきます。 ここから先は、「動けばOK」から一歩進んで、「エラーが起きても落ちないコード」「原因が分かるコード」を意識していく段階になります。

エラーの種類をざっくり理解する

まず、「エラーにはいくつか種類がある」ということを押さえておきます。 大きく分けると、次のようなイメージです。

  • 構文エラー(SyntaxError): コードの書き方そのものが間違っているときに出るエラーです。 例:カッコの閉じ忘れ、コロンの付け忘れなど。
  • 実行時エラー(例外): 書き方は正しいけれど、「実行してみたら問題が起きた」ときに出るエラーです。 例:0で割った、存在しないファイルを開こうとした、数値に変換できない文字列を int() した、など。

例外処理で扱うのは、この「実行時エラー」の方です。 Pythonでは、実行時エラーが起きると「例外(Exception)」が発生し、何もしないとプログラムはそこで止まってしまいます。

try と except の基本パターン

例外処理の基本形は、次のような tryexcept の組み合わせです。

try:
    # エラーが起きるかもしれない処理
    x = int(input("数字を入力してください: "))
    print(f"入力された数字は {x} です。")
except ValueError:
    # 特定の種類のエラーが起きたときの処理
    print("数字に変換できない入力でした。")
Python

ここでの流れはこうです。

  1. try: の中の処理を実行する
  2. もし int() のところで ValueError が発生したら、
    • try の残りは実行されず、
    • 対応する except ValueError: のブロックが実行される
  3. エラーが起きなければ、except はスキップされる

このように、try ブロックは「挑戦する場所」、except ブロックは「失敗したときの安全な着地場所」というイメージです。

複数の except でエラーごとに対応を変える

エラーには種類があるので、「エラーの種類ごとに違う対応をしたい」という場面がよくあります。

try:
    # ユーザーからファイル名を入力してもらい、そのファイルを開く
    filename = input("読み込みたいファイル名を入力してください: ")
    with open(filename, "r", encoding="utf-8") as f:
        content = f.read()
        print("ファイルの中身:")
        print(content)
except FileNotFoundError:
    # ファイルが見つからないとき
    print("指定されたファイルが見つかりませんでした。ファイル名を確認してください。")
except PermissionError:
    # 権限がなくて開けないとき
    print("そのファイルを開く権限がありません。権限設定を確認してください。")
except Exception as e:
    # その他の予期しないエラー
    print("予期しないエラーが発生しました。")
    print(f"詳細: {e}")
Python

ポイントは次の通りです。

  • except FileNotFoundError: 「ファイルが存在しない」という特定のエラーにだけ反応します。
  • except PermissionError: 「権限がない」という別の種類のエラーにだけ反応します。
  • except Exception as e: それ以外のすべての例外をまとめて受け止めます。 e にはエラーの詳細が入るので、ログに残したり画面に表示したりできます。

セキュリティや信頼性の観点では、「予期しないエラーをそのまま画面に生の形で出さない」ことも重要です。 ここでは学習用なので表示していますが、実際のサービスではログにだけ残すなどの工夫が必要になります。

else で「エラーがなかったときだけ」実行する処理を書く

tryexcept に加えて、else を使うと、「エラーが起きなかったときだけ実行する処理」を分けて書くことができます。

try:
    x = int(input("割り算の分子(上の数)を入力してください: "))
    y = int(input("割り算の分母(下の数)を入力してください: "))
    result = x / y  # ここで ZeroDivisionError が起きる可能性があります
except ValueError:
    print("数字以外が入力されました。")
except ZeroDivisionError:
    print("0で割ることはできません。分母には0以外を入力してください。")
else:
    # ここは「エラーが1つも起きなかった場合」にだけ実行されます
    print(f"割り算の結果は {result} です。")
Python

この構造の意味はこうです。

  • try の中でエラーが起きた場合 → 対応する except が実行され、else はスキップされます。
  • try の中でエラーが1つも起きなかった場合 → except はスキップされ、else が実行されます。

else を使うと、「正常系の処理」と「エラー時の処理」をきれいに分けられるので、 コードの読みやすさがぐっと上がります。

finally で「必ず実行したい処理」を書く

finally は、「エラーが起きても起きなくても、最後に必ず実行される」ブロックです。 主に「後片付け」に使われます。

try:
    f = open("data.txt", "r", encoding="utf-8")
    content = f.read()
    print("ファイルの中身:")
    print(content)
except FileNotFoundError:
    print("data.txt が見つかりませんでした。")
except Exception as e:
    print("予期しないエラーが発生しました。")
    print(f"詳細: {e}")
finally:
    # f が開かれていれば閉じる(後片付け)
    try:
        f.close()
        print("ファイルを閉じました。")
    except NameError:
        # f が定義されていない場合(openに失敗した場合など)
        print("ファイルは開かれていないため、閉じる必要はありません。")
Python

ここでのポイントは、

  • finally ブロックは、try の中でエラーが起きても起きなくても必ず実行される
  • ファイルやネットワーク接続など、「開いたら必ず閉じるべきもの」の後片付けに向いている

実務では、with open(...) as f: のような構文を使うことで自動的に後片付けしてくれることも多いですが、 finally の考え方は、例外処理の基本として知っておく価値があります。

raise で「自分から例外を投げる」

raise は、「自分で例外を発生させる」ためのキーワードです。 「この条件のときはエラーとして扱いたい」というルールを、自分で定義できます。

シンプルな例

def divide(a, b):
    if b == 0:
        # 自分で例外を発生させる
        raise ZeroDivisionError("分母が0の割り算は許可されていません。")
    return a / b

try:
    result = divide(10, 0)
    print(result)
except ZeroDivisionError as e:
    print("エラーが発生しました。")
    print(f"詳細: {e}")
Python

ここでは、

  • divide() 関数の中で、「分母が0ならエラーにする」というルールを自分で決めています。
  • raise ZeroDivisionError("メッセージ") によって、意図的に例外を発生させています。
  • 呼び出し側では、tryexcept でその例外を受け止めています。

独自のチェックルールを作る例

def set_age(age):
    if age < 0:
        raise ValueError("年齢は0以上でなければなりません。")
    if age > 150:
        raise ValueError("年齢が現実的な範囲を超えています。")
    print(f"年齢は {age} 歳に設定されました。")

try:
    set_age(-5)
except ValueError as e:
    print("年齢の設定中にエラーが発生しました。")
    print(f"詳細: {e}")
Python

このように、raise を使うことで、

  • 「この条件は許容しない」というルールをコードに埋め込む
  • 呼び出し側に「ここで問題が起きた」と知らせる

ことができます。

セキュリティの観点でも、「不正な入力や危険な状態を見つけたら、静かに無視するのではなく、明示的に例外を投げてログに残す」という設計は非常に重要です。

例外処理のテンプレートをまとめて見る

最後に、tryexceptelsefinallyraise を組み合わせたテンプレート的なコードを示します。

def safe_divide(a_text, b_text):
    """
    文字列として渡された2つの値を安全に割り算する関数です。
    エラーがあれば例外を投げます。
    """
    # 入力チェック
    if not a_text.isdigit() or not b_text.isdigit():
        raise ValueError("分子と分母は数字でなければなりません。")

    a = int(a_text)
    b = int(b_text)

    if b == 0:
        raise ZeroDivisionError("分母に0は指定できません。")

    return a / b


try:
    # ユーザーから入力を受け取ります
    a_text = input("分子(上の数)を入力してください: ")
    b_text = input("分母(下の数)を入力してください: ")

    result = safe_divide(a_text, b_text)
except ValueError as e:
    print("入力エラーが発生しました。")
    print(f"詳細: {e}")
except ZeroDivisionError as e:
    print("計算エラーが発生しました。")
    print(f"詳細: {e}")
except Exception as e:
    print("予期しないエラーが発生しました。")
    print(f"詳細: {e}")
else:
    # 正常に計算できた場合だけ結果を表示します
    print(f"割り算の結果は {result} です。")
finally:
    # ここには、ログを残したり、後片付けをしたりする処理を書けます
    print("safe_divideの処理が終了しました。")
Python

このコードには、

  • try で「エラーが起きるかもしれない処理」を囲む
  • except で「エラーの種類ごとに対応」を分ける
  • else で「エラーがなかったときだけ実行する処理」を書く
  • finally で「必ず実行したい後処理」を書く
  • raise で「自分でルール違反を例外として通知する」

という、例外処理の基本パターンがすべて詰まっています。

Day 11のまとめ

Day 11では、例外処理を通して、

  • エラーの種類(構文エラーと実行時エラー)の違い
  • tryexcept で「落ちないコード」を書く基本
  • 複数の except でエラーごとに対応を変える考え方
  • else で「正常系の処理」を分けて書くメリット
  • finally で「必ず実行したい後片付け」を書く役割
  • raise で「自分のルール違反を例外として通知する」設計

をステップバイステップで見てきました。

ここから先は、「動くコード」だけでなく、「壊れにくくて原因が分かるコード」を意識していくフェーズに入ります。 例外処理は、そのための土台になる考え方ですので、ぜひ小さなプログラムでも積極的に tryexcept を使ってみてください。


Day 11:例外処理で「入力エラー対応プログラム」を作る

Day 11の制作編では、「入力エラー対応プログラム」を題材にして、 ユーザー入力に対して安全に振る舞うコードを、tryexceptelsefinallyraise を使って作っていきます。 「間違った入力が来ても落ちない」「原因が分かる」プログラムを目指して、一歩ずつ組み立てていきます。

エラーの種類を入力の文脈でイメージする

まず、「入力エラー対応プログラム」で遭遇しやすいエラーをイメージしておきます。

  • 構文エラー(SyntaxError) コードの書き方そのものが間違っているときに出るエラーです。 例:カッコの閉じ忘れ、コロンの付け忘れなど。これは「書く側のミス」です。
  • 実行時エラー(例外) コードの書き方は正しいけれど、「入力された値が想定外」で問題が起きるエラーです。 例:数字に変換できない文字列を int() した、0で割った、存在しないファイル名が入力された、など。

入力エラー対応プログラムで扱うのは、この「実行時エラー(例外)」です。 ユーザーの入力は必ずしもきれいではないので、「例外処理」はほぼ必須の考え方になります。

try と except で「落ちない入力処理」を作る

まずは、数字入力に対する基本的な例外処理から始めます。

# ユーザーに数字を入力してもらい、安全に整数に変換する例

user_input = input("年齢を数字で入力してください: ")

try:
    age = int(user_input)  # ここで ValueError が起きる可能性があります
    print(f"あなたの年齢は {age} 歳ですね。")
except ValueError:
    # 数字に変換できない場合の処理
    print("数字に変換できない入力でした。半角数字で年齢を入力してください。")
Python

ここでの流れは次の通りです。

  • try: の中で「エラーが起きるかもしれない処理」を実行します。
  • int(user_input) が数字に変換できない場合、ValueError という例外が発生します。
  • その瞬間、try の残りは実行されず、対応する except ValueError: が実行されます。
  • エラーが起きなければ、except はスキップされ、通常通り処理が進みます。

この構造だけでも、「変な入力でプログラムが止まる」状態から、「丁寧に注意を返す」状態へと変えることができます。

複数の入力と複数のエラーを扱う

次に、「2つの数字を入力してもらい、割り算をする」場面を考えます。 ここでは、数字変換のエラーと、0で割るエラーの両方に対応します。

# 割り算入力の例外対応プログラム

num_text = input("分子(上の数)を数字で入力してください: ")
den_text = input("分母(下の数)を数字で入力してください: ")

try:
    # まず文字列を整数に変換します
    numerator = int(num_text)
    denominator = int(den_text)

    # 次に割り算を行います(ここで ZeroDivisionError の可能性)
    result = numerator / denominator

except ValueError:
    # 数字に変換できない場合
    print("数字以外の入力が含まれています。半角数字のみを入力してください。")
except ZeroDivisionError:
    # 分母が0の場合
    print("分母に0は指定できません。0以外の数字を入力してください。")
else:
    # どの例外も発生しなかった場合だけ結果を表示します
    print(f"割り算の結果は {result} です。")
finally:
    # ここは必ず実行されます(後処理やログに向いています)
    print("入力処理を終了しました。")
Python

ここでのポイントは次の通りです。

  • except ValueError: 「数字に変換できない入力」にだけ反応します。
  • except ZeroDivisionError: 「分母が0の割り算」にだけ反応します。
  • else: どの例外も発生しなかった場合にだけ実行される「正常系」の処理です。
  • finally: エラーの有無に関係なく必ず実行されるブロックで、「処理の終了メッセージ」や「ログ記録」に向いています。

この形が、「入力エラー対応プログラム」の基本パターンになります。

raise で「自分のルール違反」をエラーとして扱う

次に、「入力値が形式的には正しいけれど、ビジネスルール的に許容できない」というケースを考えます。 例えば、「年齢は0〜120歳の範囲に限定したい」といったルールです。

ここで raise を使うと、「自分で例外を発生させて、呼び出し側に知らせる」ことができます。

def parse_age(age_text):
    """
    年齢入力を安全に処理する関数です。
    数字でない場合や、範囲外の場合は例外を投げます。
    """
    if not age_text.isdigit():
        # 数字でない場合は ValueError を投げる
        raise ValueError("年齢は半角数字で入力してください。")

    age = int(age_text)

    if age < 0:
        raise ValueError("年齢は0以上でなければなりません。")
    if age > 120:
        raise ValueError("年齢が現実的な範囲を超えています。")

    return age


try:
    user_input = input("年齢を入力してください: ")
    age = parse_age(user_input)  # ここで必要に応じて例外が投げられます
except ValueError as e:
    # parse_ageが投げたエラーを受け取ってメッセージを表示します
    print("入力エラーが発生しました。")
    print(f"詳細: {e}")
else:
    print(f"年齢は {age} 歳として受け付けました。")
finally:
    print("年齢入力処理を終了しました。")
Python

この構造の意味はこうです。

  • parse_age() 関数の中で、「許容しない入力」を見つけたら raise ValueError("メッセージ") で例外を投げます。
  • 呼び出し側では、tryexcept でその例外を受け止め、ユーザーに分かりやすいメッセージを返します。
  • こうすることで、「ルール違反を静かに無視する」のではなく、「明示的にエラーとして扱う」ことができます。

入力エラー対応プログラムでは、「何がダメなのか」をはっきりさせることが重要です。 raise は、そのための強力な道具になります。

入力エラー対応プログラムのテンプレート(コメント付き)

最後に、Day 11の内容をまとめた「入力エラー対応プログラム」のテンプレートを示します。

# 入力エラー対応プログラム(テンプレート)

def safe_int(text):
    """
    文字列を安全に整数に変換する関数です。
    数字でない場合は ValueError を投げます。
    """
    if not text.isdigit():
        raise ValueError("半角数字のみを入力してください。")
    return int(text)


def safe_divide(num_text, den_text):
    """
    2つの文字列を安全に割り算する関数です。
    数字でない場合や、分母が0の場合は例外を投げます。
    """
    numerator = safe_int(num_text)   # ここで ValueError の可能性
    denominator = safe_int(den_text) # ここで ValueError の可能性

    if denominator == 0:
        raise ZeroDivisionError("分母に0は指定できません。")

    return numerator / denominator


# メインの処理
try:
    num_text = input("分子(上の数)を入力してください: ")
    den_text = input("分母(下の数)を入力してください: ")

    result = safe_divide(num_text, den_text)

except ValueError as e:
    # 数字入力に関するエラー
    print("入力エラーが発生しました。")
    print(f"詳細: {e}")

except ZeroDivisionError as e:
    # 0で割ろうとしたときのエラー
    print("計算エラーが発生しました。")
    print(f"詳細: {e}")

except Exception as e:
    # その他の予期しないエラー
    print("予期しないエラーが発生しました。")
    print(f"詳細: {e}")

else:
    # 正常に計算できた場合だけ結果を表示します
    print(f"割り算の結果は {result} です。")

finally:
    # 必ず実行される後処理
    print("入力エラー対応プログラムを終了します。")
Python

このテンプレートには、

  • try で「エラーが起きるかもしれない入力処理」を囲む
  • except で「エラーの種類ごとにメッセージを変える」
  • else で「正常に終わったときだけ結果を表示する」
  • finally で「終了メッセージやログを必ず出す」
  • raise で「自分のルール違反を例外として通知する」

という、入力エラー対応の基本パターンがすべて含まれています。

Day 11(制作編)のまとめ

Day 11の制作編では、「入力エラー対応プログラム」を通して、

  • ユーザー入力に対して例外処理を組み込む考え方
  • tryexcept で「落ちない入力処理」を作る基本
  • elsefinally で「正常系」と「後処理」を分けて書くメリット
  • raise で「ルール違反を明示的なエラーとして扱う」設計

を体験していただきました。

ここまで来ると、「入力は必ずしも正しいとは限らない」「それでもプログラムは落とさず、理由を伝えるべきだ」という感覚が少し育っているはずです。 この感覚は、今後の90日間で作るあらゆるプログラムにとって、信頼性とセキュリティの土台になっていきます。

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