python 例外処理のベストプラクティス

python Python

他の言語にも共通する一般的なベストプラクティス

  • 具体的な例外を補足する
    できるだけ特定の例外を補足するようにします。except Exception: のように抽象度が高い例外よりも except: FileNotFoundError: のように特定の具体的な例外として補足します。
  • 例外処理は限定的に使う:
    例外を事前に予測できるような制御フローとして扱わない。つまり、通常の条件分岐で処理できるようなロジックに例外処理を使わないようにします。例外処理は予期しない事態に対応するために使用するものです。制御フローとしての過度な利用は可読性を損ね、場合によってはパフォーマンスを損ねます。
  • リソースの開放を保証する:
    ファイルのクローズ、接続の切断など、リソースの開放を確実に実行します。
  • ログを残す:
    例外の原因特定やデバッグのために例外発生時には必ずログを記録します。
  • 例外の再送出:
    例外の補足後に処理しきれない場合は、例外を再送出して上位レイヤーでの処理を行うことを視野に入れます。

python独自のベストプラクティス

  • EAFP (Easier to Ask for Forgiveness than Permission) スタイルを推奨:
    Pythonでは、「許可を求めるよりも、許しを請う方が簡単」というEAFPの原則が推奨されます。これは、操作を実行する前に条件をチェックする(LBYL: Look Before You Leap)のではなく、まず操作を試行し、失敗した場合に例外を処理するというアプローチです。

LBYL(Look Before You Leap): 事前に条件を確認する方式。C#やJavaなどでは主流だが、Pythonではやや冗長になりがち。

Python
# LBYL版(あまりPython的ではない)
if "key" in my_dict:
    value = my_dict["key"]
else:
    value = "default"

# EAFP版(Pythonでは推奨される)
try:
    value = my_dict["key"]
except KeyError:
    value = "default"
  • else ブロックを try-except に使用する:
    try-except ブロックにはオプションの else ブロックを追加できます。この else ブロックは、try ブロック内で例外が発生しなかった場合にのみ実行されます。これは、try ブロックで例外的な状況を捕捉し、通常の成功時の処理を else ブロックに記述することで、コードの可読性を高めるのに役立ちます。
Python
try:
    result = risky_operation()
except SomeError as e:
    print("Error occurred:", e)
else:
    print("Operation succeeded:", result)
finally:
    cleanup()
  • 例外の連鎖 (raise ... from ...) を使用する:
    Python 3から導入された raise ... from ... 構文は、ある例外が別の例外によって引き起こされたことを明示的に示すために使用されます。原因となる例外を明示的にチェーンすることで、デバッグ時にトレースが非常に分かりやすくなります。
Python
def fetch_data_from_db():
    try:
        # データベースアクセス処理
        raise ConnectionError("データベース接続に失敗しました")
    except ConnectionError as e:
        raise RuntimeError("データ取得中に接続エラーが発生しました") from e

try:
    fetch_data_from_db()
except RuntimeError as e:
    print(f"エラー: {e}")
    if e.__cause__:
        print(f"原因: {e.__cause__}")

# エラー: データ取得中に接続エラーが発生しました
# 原因: データベース接続に失敗しました
  • pythonの with ステートメントは、リソースの取得と解放を自動的に行うための強力な機能です。ファイル、ロック、データベース接続など、リソースを扱う際に ブロックを明示的に書くことなく、例外発生時にもリソースが確実にクローズされることを保証できます。
Python
try:
    with open("my_file.txt", "r") as f:
        content = f.read()
except FileNotFoundError:
    print("ファイルが見つかりません。")
# ファイルは例外の有無にかかわらずtryブロックを抜ける際に自動的に閉じられる
  • contextlib.suppressで特定の例外を明示的に無視:
    条件分岐を書く代わりに、明示的に例外を無視できるため、冗長なコードがスッキリします
Python
from contextlib import suppress

with suppress(FileNotFoundError):
    os.remove("temp.txt")

コメント

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