Pythonコードを書くとき、ファイルの読み取り、ユーザー入力の解析、ネットワークリクエストの送信など、実行時にエラーが発生することはほぼ確実です。これらのエラーを適切に処理しないと、クラッシュが発生し、ユーザーを苛立たせ、デバッグが悪夢になる可能性があります。そこでPythonの例外処理がtry/except
まさに命綱となります。 、、、コンテキストマネージャ( )を使いこなすことで、else
問題を適切に処理し、本当に重要なバグを表面化させる、回復力が高くクリーンなコードを書くことができます。finally
with
このガイドでは、Pythonで例外を処理するための必須のベストプラクティスを網羅します。実際のコードスニペット、それぞれのアプローチをいつ、なぜ使うべきかの説明、そして将来多くの頭痛の種を回避できるヒントをいくつかご紹介します。ガイドを読み終える頃には、これらのテクニックが、予期せぬ事態が発生してもプログラムをスムーズに実行し続けるためにどのように役立つかが分かるでしょう。
Pythonで例外を効果的に処理する方法
方法 1 – 特定の例外をキャッチする (すべてをキャッチするのは必ずしも良い考えではないため)
これは適切なエラー処理の基本です。すべてをむき出しの で捕捉するのではなくexcept
、想定した例外だけを捕捉しましょう。これにより、バグの隠蔽を防ぎ、デバッグが容易になります。例えば、数値であるはずのユーザー入力を読み取っている場合、捕捉はValueError
理にかなっています。
raw = input("Enter a number: ") try: number = int(raw) print("You entered:", number) except ValueError: print("That wasn’t a valid number.")
セットアップによっては、特にリカバリ方法が同一である場合、複数の関連する例外をキャッチすると便利です。例えば、次のようにします。
try: choice = ["apple", "pear", "banana"][int(raw)] except (ValueError, IndexError) as e: print("Invalid choice:", type(e).__name__)
FileNotFoundError
遭遇する最も一般的な例外としては、、、などがありますValueError
。KeyError
これらの例外がいつ発生するかを知っておくと、特にデバッグ時に非常に役立ちます。
方法2 – とを使用しelse
てfinally
きれいに分離する
else
とを忘れてしまう人は多いですfinally
。Else
はエラーが発生しなかった場合にのみ実行されるので、「成功」コードを置くのに適しています。Finally
は常に実行されるので、ファイルを閉じたり、リソースを解放したりといったクリーンアップに最適です。少し奇妙に思えるかもしれませんが、場合によっては、すべてを整理するために両方が必要になることがあります。
try: with open("config.json", "r", encoding="utf-8") as f: data = f.read() except FileNotFoundError: print("No config file found.") else: print("Loaded", len(data), "bytes.")
より慎重にしたい場合は、finally
例外が発生してもファイルが閉じられるように、 を使用します。
f = None try: f = open("config.json", "r", encoding="utf-8") data = f.read() except OSError as err: print("OS error:", err) finally: if f: f.close()
このパターンは、カスタムリソース管理を扱う場合など、設定によってはwith
ブロックが期待通りに動作しない可能性があるため便利です。そのため、これはフォールバックアプローチであることに留意してください。
方法3 – メインワークフローをガードブロックで囲む
予期せぬバグが紛れ込んでしまう可能性がある場合など、トップレベルのエラーキャッチャーがあると便利な場合があります。例えば、以下のようになります。
import logging, sys logging.basicConfig(level=logging. INFO) def main(): # core program code return 0 if __name__ == "__main__": try: code = main() except Exception: logging.exception("Unhandled error occurred") sys.exit(1) sys.exit(code)
こうすることで、重大なエラーが発生した場合、詳細なログが記録され、プログラムはゼロ以外のステータスで終了し、自動化ツールやスクリプトにエラーを通知します。これは便利ですが、エラーを無視してよいというわけではありません。あくまでも最後の手段、安全策です。
方法4 – リソースのコンテキストマネージャーを活用する
ファイルやソケットを扱っていますか?with
クリーンアップを自動的に管理するので、ぜひ使ってみてください。例えば:
from pathlib import Path try: with Path("data.txt").open("r", encoding="utf-8") as f: print(f.readline().strip()) except FileNotFoundError: print("Create data.txt first.")
これは、手動でファイルを開いたり閉じたりするよりもはるかにクリーンであり、読み取り中に例外が発生した場合にファイル ハンドルが開いたままになる可能性が低くなります。
方法5 – 例外の発生、再発生、連鎖
例外をキャッチしたあとに、コンテキストを追加したり、再度スローしたりしたい場合があります。例えば、チェックが失敗した場合などです。
def parse_age(s: str) -> int: if not s.isdigit(): raise ValueError("Age must contain only digits") age = int(s) if age < 0: raise ValueError("Age cannot be negative") return age
OSError をキャッチしたが、ログに記録した後にそれを伝播させることに決めた場合は、再発生させるのが方法です。
import logging try: do_risky_thing() except OSError as err: logging.error("OS error: %s", err) raise
例外をチェーンすると、from
データの読み込み時やモジュールの呼び出し時に便利です。
from pathlib import Path import json def load_json(path: str): try: text = Path(path).read_text(encoding="utf-8") except OSError as e: raise RuntimeError(f"Failed to read {path}") from e return json.loads(text)
理由はよく分かりませんが、これによりトレースバック情報がそのまま保持され、実際に何が間違っていたのかがわかります。
方法6 – 予期しないエラーをキャッチオールする(控えめに使用)
すべてがうまくいかない場合は、奇妙な失敗を記録するための幅広い設定も可能ですが、やりすぎには注意が必要です。例えば、
import traceback try: risky() except Exception as e: print(f"Unexpected {type(e).__name__}: {e}") traceback.print_exc()
ただし、をキャッチする際には注意が必要ですBaseException
。これにはシステムシャットダウン信号も含まれます。何らかのシャットダウン手順を実行している場合にのみ、 をキャッチしてください。
try: service_loop() except BaseException as e: print(f"Caught {type(e).__name__}; shutting down cleanly.") raise
方法 7 — 複数のエラーの処理 (Python 3.11+)
Python 3.11 を使用している場合、ExceptionGroup
特にバッチ処理で、複数のエラーを一度に処理するのが簡単になります。
def run_tests(tests): errors = [] for t in tests: try: t.run() except Exception as e: e.add_note(f"Test {t.name} failed") errors.append(e) if errors: raise ExceptionGroup("Batch failures", errors)
特定のエラー クラスをキャッチすると、except*
より細かい制御が可能になります。
try: run_tests(tests) except* (ValueError, TypeError): print("Some data errors occurred.") except* OSError: print("Some OS errors occurred.")
一般的なトラブルシューティングのヒント
- 名前解析エラー–
int()
またはfloat()
で囲まれていますtry/except ValueError
。 - ファイル アクセスの問題– catch
FileNotFoundError
またはPermissionError
。 - API またはネットワーク エラー– タイムアウト、再試行、接続拒否を処理します。
- 常に例外をログに記録します。これにより、後でデバッグがはるかに簡単になります。
まとめ
例外処理を適切に行うことは、クラッシュを回避することだけではありません。クリーンでメンテナンス性が高く、回復力のあるPythonコードを書くことが重要です。特定のcatch、コンテキストマネージャ、そしてトップレベルのハンドラを使用することで、問題が発生してもアプリがクラッシュするのではなく、正常に動作するようになります。これらのテクニックを練習し続ければ、エラー処理は頭痛の種ではなく、安全網のように感じられるようになるでしょう。このテクニックが、誰かが数時間も頭を悩ませる時間を省くのに役立つことを願っています!
まとめ
- バグが隠されることを避けるために特定の例外をキャッチします。
- 明確な制御フローとクリーンアップのために
else
とを使用します。finally
- リソース管理にコンテキスト マネージャーを活用します。
- メインワークフローをトップレベルのエラー処理でラップします。
- 例外を再度発生させて連鎖させ、コンテキストを追加します。
- 広範囲にわたる包括的なブロックを過度に使用せず、慎重に検討してください。
- Python 3.11 以降では、
ExceptionGroup
バッチ エラーに使用します。