Python | Web / API:assert

Python Python
スポンサーリンク

概要(assertは「前提が破れたら即座に止める」ための開発時チェック)

assertは「この時点では必ず条件が成り立つはず」という前提をコードに埋め込み、破れたら AssertionError を投げて即座に気付ける仕組みです。主な用途は開発・テスト・内部整合性チェックで、ユーザー入力の検証や本番向けのエラー処理には向きません。基本形は assert 条件, “メッセージ”。条件が False のときだけ止まり、メッセージがヒントになります。


基本の使い方(構文・メッセージ・前提の埋め込み)

最短の構文とメッセージ付き

x = 10
assert x >= 0, "xは0以上であるべき"
Python
  • 挙動: 条件がTrueなら何もしない。FalseならAssertionErrorで停止し、メッセージを表示します。
  • 狙い: 「この時点でこうであるべき」をコード内に明示し、破れた瞬間に原因へ直行します。

関数の前提(契約)を明示する

def divide(a: float, b: float) -> float:
    assert isinstance(a, (int, float)) and isinstance(b, (int, float)), "数値のみ受け付ける"
    assert b != 0, "bは0であってはならない"
    return a / b
Python
  • 効果: 異常系は入口で即発見。内部ロジックの整合性が崩れた時点で止まるため、バグの位置がズレません。

重要ポイントの深掘り(assertの性質・使いどころ・禁じ手)

実行最適化で無効化される(-O で消える)

  • 事実: python -O(最適化)で実行すると、assert文は評価されず消えます(debug が Falseになるため)。
  • 含意: 本番で前提チェックを頼る用途は不適切。ユーザー入力の検証や外部依存の失敗には try/except や明示の raise を使うべきです。

使う場面(内部整合性・不変条件・テスト補助)

  • 内部整合性: 「この配列は必ずソート済み」「このIDは非空」など、コードの契約を表現。
  • 不変条件(invariant): ループや状態遷移の途中で「常に成立すべき条件」を監視。
  • テスト補助: 最小例の中で条件を埋め、破れたら即停止して原因に直行。

使わない場面(外部入力・運用エラー)

  • 外部入力: フォームやAPIのリクエスト検証はassertではなくバリデーション(if/raise)で。
  • 可用性: 落とさずにリトライ・代替処理したい場面では、例外処理の設計を優先。

Web / API 文脈の例(契約を明示して事故を早期発見)

リクエスト前の前提確認(URL・タイムアウト)

import requests

def get_json(url: str, timeout: float = 5.0):
    assert url.startswith("https://"), "HTTPSのみ許可"
    assert timeout > 0, "timeoutは正の値"
    r = requests.get(url, timeout=timeout)
    r.raise_for_status()
    return r.json()
Python
  • ポイント: 内部前提(HTTPS、正のタイムアウト)をassertで明示。外部失敗はraise_for_statusで扱う。

受信データの構造を一部保証(最低限のキー存在)

def parse_user(data: dict) -> dict:
    assert isinstance(data, dict), "辞書であるべき"
    assert "id" in data and "name" in data, "必須キーが存在するべき"
    return {"id": data["id"], "name": data["name"]}
Python
  • 狙い: 「ここまで来たら必ずあるべき」内部契約を守れない場合に即停止。

状態遷移の不変条件

def process_queue(q: list[str]):
    assert all(isinstance(x, str) for x in q), "キューは文字列のみ"
    while q:
        item = q.pop()
        assert item, "空文字は入らない前提"
        # 処理...
Python
  • 効果: 途中で不変条件が破れた瞬間に原因へ直行。バグの増幅を防ぎます。

実務の型(assertとraiseの使い分け・段階的導入)

開発時の保険として“薄く広く”埋める

  • 入口: 型・必須キー・範囲など「内部契約」をassertで明示。
  • 中間: ソート・ユニーク性・非空などの不変条件に軽いassert。
  • 出口: 返却値の型・キー存在などの最小保証をassert。

ユーザー向けの失敗は明示的にraise

def fetch_user(uid: str):
    if not uid:
        raise ValueError("uidは必須です")  # ← ユーザー入力の検証(assertではない)
    # 通信・例外処理...
Python
  • 線引き: 「外界の不確実さ」にはif/raise/try-except、「内部で保証すべき契約」にはassert。

断定をテストへ移す(pytestのassert)

  • テストでは: pytestの assert を使って期待結果を検証し、プロダクションコードのassertは「内部契約の表明」に限定します。

例題で身につける(境界・型・不変条件)

例題1:境界条件の契約

def is_premium(score: int) -> bool:
    assert isinstance(score, int), "scoreは整数"
    assert 0 <= score <= 200, "scoreは0〜200"
    return score >= 100
Python
  • 目的: “受け取りうる範囲”を明示。境界でのバグを即発見。

例題2:IDとスキーマの最小保証

def normalize_user(u: dict) -> dict:
    assert isinstance(u, dict), "辞書のみ"
    assert "id" in u and u["id"], "idは必須・非空"
    name = u.get("name") or "unknown"
    return {"id": str(u["id"]), "name": name}
Python
  • 効果: 後段で「id前提」コードが壊れない。

例題3:ソート前提のアルゴリズム

def unique_sorted(xs: list[int]) -> list[int]:
    assert xs == sorted(xs), "入力は事前ソート済みの前提"
    out = []
    prev = None
    for x in xs:
        if x != prev:
            out.append(x)
        prev = x
    return out
Python
  • 狙い: 前提が破れたら即停止→前段の責務に戻って修正。

落とし穴と回避(副作用・重い条件・メッセージ設計)

副作用を持つ条件式は書かない

  • NG: assert mutate(data), “…”(評価有無で動作が変わる)
  • OK: 条件は純粋(副作用なし)にする。assertの有無で挙動が変化してはならない。

重いチェックは避ける(本番で無効化されうる)

  • 原則: O(n)や外部I/Oを伴うassertは避ける。軽量で純粋な条件に限定する。

メッセージは「何が、どう違う」を短く

  • 良い例: “bは0であってはならない”
  • 悪い例: “error”(情報がない)

まとめ(assertは“内部契約の表明”に使う)

assertは、コード内部の「この時点で成立すべき前提」を短く明示し、破れた瞬間に原因へ直行するための開発・テスト向けの道具です。-Oで無効化される性質から、ユーザー入力や運用エラーの扱いには向きません。線引きは「外界→if/raise/例外処理」「内部契約→assert」。入口・中間・出口に薄く配置し、条件は軽量・純粋・具体的なメッセージ付きにする。この型を守れば、初心者でも“壊れる前提”を即座に掴み、修正までの距離を一気に縮められます。

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