🧠 推測に頼るコード(Refuse the temptation to guess)
🧭 はじめに
このページでは、Python の Zen Refuse the temptation to guess に反するアンチパターン、 「推測に頼るコード」 を扱います。
Python は柔軟で表現力が高い言語ですが、その反面 「それっぽく書けてしまう」 ことが設計ミスを隠します。 本記事では、その曖昧さがどのように事故につながるかを整理します。
🎯 ねらい
- Python の truthy / falsy 文化が設計と衝突する瞬間を可視化する
- 「短い条件式」と「正しい仕様」は別物であることを示す
❓ 問題の定義
推測に頼るコードとは、
- 値の意味を条件式の雰囲気で判断し
- 未指定・無効・有効を曖昧に混同
- 読み手(あるいは未来の自分)が 「きっとこういう前提だろう」と推測しないと理解できない
状態を指します。
推測は人間が補完しているだけで、コードの仕様にはなっていません。
🤒 よくある症状
⚖️ truthy / falsy に丸投げ
if value:
do_something(value)
0''[]False
これらはすべて falsy。
→ 意味の違いが条件式で潰れる
🫥 None と空値の同一視
None(未指定)[](指定されたが空)0(有効な数値)
が同じ扱いになる。
📐 前提があるのにチェックしない
- 型は int のはず
- 範囲は正のはず
- 未指定は来ないはず
→ 「はず」をコードに書いていない
前提を書かないコードは、前提が壊れた瞬間に嘘をつき始めます。
❌ 悪い例①:ページングの罠
def fetch_items(page=1):
if page:
offset = (page - 1) * 20
else:
offset = 0
return query(offset)
何が問題か
page=0が falsy- 結果として 意図せず 1 ページ目扱い
- バグなのに例外も出ない
❌ 悪い例②:金額 0 円が「未入力」になる
def apply_discount(amount):
if amount:
return amount * 0.9
return None
amount=0→ 未入力扱い- 無料・全額割引・返金処理で破綻
✅ 良い例①:is None を使う
def fetch_items(page=None):
if page is None:
page = 1
offset = (page - 1) * 20
return query(offset)
改善点
- 未指定 (
None) と有効値を分離 - 条件の意図が一意に読める
- 推測が不要
`if x is None:` は「設計意思」を明示する構文です。
✅ 良い例②:sentinel を定義する
UNSET = object()
def apply_discount(amount=UNSET):
if amount is UNSET:
return None
return amount * 0.9
ポイント
- 「未指定」を明確な状態として表現
- falsy な正当値と衝突しない
- API の意味が安定する
🧩 設計の要点
曖昧さは条件式でなく仕様で解決する
- 未指定か?
- 無効か?
- 有効な値か?
これらは 値の雰囲気ではなく、型や契約で表す。
境界は必ず明示する
- 関数の入口で検証
- 型ヒント・バリデーション
- 失敗は例外か明示的な戻り値
✅ チェックリスト
if x:のxは0/ 空 /Falseを取り得るか- 未指定と有効値を 同じ表現で扱っていないか
- 前提条件(型・範囲)が コードに書かれているか
- 呼び出し側が「察し」を要求されていないか
🧠 まとめ
推測に頼るコードは、
- 短く
- それっぽく
- 一見 Pythonic
に見えます。
しかし Python の Zen が言う Refuse the temptation to guess とは、
書き手の推測を減らせ、ではなく 読み手に推測させるな
という設計原則です。
これで前半4本が揃いました。 次章では、🧩 過剰抽象・早すぎる一般化に進めます。