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")→ 業務ロジック は UserNotFound、HTTP 層 で 404 に翻訳
drill
drill/ — TaskNotFound を定義して find_task から投げる
よくある詰まり
| 症状 | 原因 | 対処 |
|---|---|---|
TypeError: exceptions must derive from BaseException | class 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 モジュール |
力試ししたい方は クイズ形式の演習(任意) もあります。読むだけでも十分です。