採点されません。匿名です。 間違えることは学習の一部です。読むだけでもかまいません。

L08 ch02 — カスタム例外

テクノロジ系 / L08 exception — 例外処理

L08 ch02 — カスタム例外

「対象が見つからない」「在庫が足りない」を専用エラー型にする

ゴール

  • Exception を継承して独自の例外クラスを定義できる
  • raise で自作例外を投げられる
  • except MyError as e: で型ベースに捕捉できる
  • 何のために自作するかを説明できる

なぜカスタム例外?

組み込み例外 (ValueError 等) を流用すると…

def find_user(uid):
    raise ValueError("not found")  # ← 入力値の問題? 検索結果? 区別不能

呼び出し側が「どのエラーか」を文字列で判別することになる ← 脆い

except ValueError as e:
    if "not found" in str(e):  # ❌ メッセージ文言に依存
        ...

最小例

class UserNotFound(Exception):
    """ユーザーが見つからない"""

def find_user(uid: int) -> dict:
    users = {1: {"name": "Taro"}}
    if uid not in users:
        raise UserNotFound(f"user {uid} not found")
    return users[uid]

で区別できる → 呼び出し側がシンプル:

try:
    find_user(99)
except UserNotFound as e:
    print(f"見つからない: {e}")

命名規約

  • クラス名は PascalCase + "Error" もしくは具体名

- UserNotFound, InvalidPriceError, OutOfStock

  • 末尾 Exception基底クラス用 (Exception 自体と紛らわしいので避ける)
class TaskNotFound(Exception): ...     # OK
class InvalidPriceError(Exception): ... # OK
class MyException(Exception): ...       # ❌ 名前から意図が読めない

階層を作る (発展)

複数のカスタム例外をまとめて捕まえたい時は基底を作る:

class AppError(Exception):
    """このアプリ固有のエラー基底"""

class UserNotFound(AppError): ...
class TaskNotFound(AppError): ...
class InvalidPriceError(AppError): ...
try:
    ...
except AppError as e:        # ← アプリ起因のものだけ一括捕捉
    log(e)
except Exception as e:       # ← 想定外 (バグ) はこちら
    alert(e)

引数で文脈を持たせる

Exception.__init__ を活用するとデバッグが楽:

class UserNotFound(Exception):
    def __init__(self, user_id: int):
        super().__init__(f"user {user_id} not found")
        self.user_id = user_id   # 後から参照できる

try:
    raise UserNotFound(99)
except UserNotFound as e:
    print(e)            # → user 99 not found
    print(e.user_id)    # → 99  (型付きで取れる)

FastAPI との接続 (予告)

カスタム例外は次の chapter (ch03-raise) で FastAPI の HTTPException と組み合わせる:

from fastapi import FastAPI, HTTPException

app = FastAPI()

@app.get("/users/{uid}")
def get_user(uid: int):
    try:
        return find_user(uid)
    except UserNotFound:
        raise HTTPException(status_code=404, detail="user not found")

業務ロジックUserNotFoundHTTP 層 で 404 に翻訳

drill

drill/TaskNotFound を定義して find_task から投げる

よくある詰まり

症状原因対処
TypeError: exceptions must derive from BaseExceptionclass Foo: (継承忘れ)class Foo(Exception):
except FooError: で捕まらない投げてるのが別クラスraise FooError(...) と型を一致させる
メッセージが空 Foo()super().__init__(msg) 忘れException に引数を渡す

まとめ

  • カスタム例外 = Exception を継承するだけ
  • 文字列でなく でエラー種別を表現する
  • アプリ全体の基底クラスを 1 つ用意すると一括捕捉できる
  • 次は raise の使い方と FastAPI の HTTPException 連携

次に学ぶ領域

領域続けて読む
raise の使い方 / 例外チェーンch03-raise (予告)
FastAPI で 404 を返すtopics/fastapi/.../http-exception
ロギングlogging モジュール
力試ししたい方は クイズ形式の演習(任意) もあります。読むだけでも十分です。