概要(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」。入口・中間・出口に薄く配置し、条件は軽量・純粋・具体的なメッセージ付きにする。この型を守れば、初心者でも“壊れる前提”を即座に掴み、修正までの距離を一気に縮められます。
