この記事のポイント

  • Literal型を定数管理の道具として正しくレビューできる
  • マジックナンバー排除と型安全を同時に実現する設計法を理解できる
  • 現場で頻発するハードコード崩壊パターンを見抜ける

そもそもLiteral型とは

Pythonの型ヒントにおけるLiteralは、指定値のみを型許容範囲と限定する仕組みです。

from typing import Literal

Status = Literal["SUCCESS", "FAIL", "TIMEOUT"]

特徴は以下です。

  • 受け入れ可能な定数値を限定
  • 型レベルでバリデーションを表現
  • 呼び出し側の安全性向上
  • マジックナンバー撲滅効果が高い

なぜこれをレビューするのか

現場では以下の失敗が多発します。

  • マジックナンバー散在放置
  • EnumとLiteralの混在崩壊
  • Literal肥大化による責務混濁
  • 定数の責務線引き曖昧化

レビューアーは「定数=設計責務の写像」として型管理を読みます。

レビューアー視点

  • Literalの定義箇所が適切に整理されているか
  • 定数分類がドメイン責務と一致しているか
  • Enumとの役割分離が整理されているか
  • 呼び出し側で定義外値が扱われていないか
  • バリデーション不要化に繋がっているか

開発者視点

  • ドメイン状態分類はLiteralで型制御
  • 機能横断定数はEnumで集中管理
  • バリデーションコスト削減に活用
  • 入力値検証はLiteral+外部層で責務分離
  • Literal肥大化防止を常に意識

良い実装例

なぜこの実装が良いのか

  • 状態分類と定数管理がドメイン責務で整理
  • 呼び出し時に定義外入力が型レベルで防止
  • 業務状態がLiteralに自然に写像
  • マジックナンバー排除が完全実現
# status_types.py

from typing import Literal

ResponseStatus = Literal["SUCCESS", "FAIL", "TIMEOUT"]

def process_response(status: ResponseStatus):
    if status == "SUCCESS":
        print("処理成功")
    elif status == "FAIL":
        print("処理失敗")
    elif status == "TIMEOUT":
        print("タイムアウト発生")
補足

状態分類はLiteralが最も自然に安全表現できる領域です。レビューではドメイン状態分類とLiteral定義が一致しているか確認します。

レビュー観点

  • 状態分類がLiteralで整理されているか
  • マジックナンバー残存箇所がないか
  • Enumとの役割整理が整理されているか
  • 呼び出し側で型安全が保証されているか
  • Literal定義肥大化が発生していないか

良くない実装例: ケース1(マジックナンバー散在放置)

# bad_magic_number.py

def process_response(status: str):
    if status == "SUCCESS":
        print("処理成功")
    elif status == "FAIL":
        print("処理失敗")
    elif status == "TIMEOUT":
        print("タイムアウト発生")
@Reviewer
マジックナンバーとして文字列が直接散在しています。Literal型定義へ集約してください。

問題点

  • 定数が各所に直書き
  • 保守性・補完性・検証性全崩壊
  • レビュー耐性消失

改善例

# good_literal_extraction.py

from typing import Literal

ResponseStatus = Literal["SUCCESS", "FAIL", "TIMEOUT"]

def process_response(status: ResponseStatus):
    if status == "SUCCESS":
        print("処理成功")
    elif status == "FAIL":
        print("処理失敗")
    elif status == "TIMEOUT":
        print("タイムアウト発生")

文字列値がコード内に散在していないかはレビューで常に即座に確認します。

良くない実装例: ケース2(EnumとLiteralの役割混在崩壊)

# bad_enum_literal_mix.py

from typing import Literal
from enum import Enum

class StatusEnum(Enum):
    SUCCESS = "SUCCESS"
    FAIL = "FAIL"
    TIMEOUT = "TIMEOUT"

ResponseStatus = Literal["SUCCESS", "FAIL", "TIMEOUT"]
@Reviewer
EnumとLiteralが同時定義され、役割分離が崩壊しています。使用用途ごとに統一整理してください。

問題点

  • 同じ責務に両者併存
  • 定数管理の責務線引き不明
  • 読み手混乱発生

改善例

# good_literal_or_enum.py

# 状態分類用途ならLiteralに統一

from typing import Literal

ResponseStatus = Literal["SUCCESS", "FAIL", "TIMEOUT"]

Enumは定数管理用途、Literalは状態分類用途で役割分離するのが原則です。レビューでは整理有無を重点確認します。

良くない実装例: ケース3(Literal肥大化による意味崩壊)

# bad_literal_bloating.py

from typing import Literal

ResponseStatus = Literal[
    "SUCCESS", "FAIL", "TIMEOUT", "RETRYING", "PENDING", "CANCELLED", "ABORTED"
]
@Reviewer
Literal定義が肥大化し始めています。状態責務を細分化して定数分離を検討してください。

問題点

  • 状態分類の粒度崩壊
  • 責務線引き不明瞭
  • 読み手の型設計理解困難化

改善例

# good_literal_grouping.py

from typing import Literal

FinalStatus = Literal["SUCCESS", "FAIL", "CANCELLED"]
InterimStatus = Literal["PENDING", "RETRYING"]
ErrorStatus = Literal["TIMEOUT", "ABORTED"]

状態粒度ごとに型を分離定義することで保守性と型安全性を維持します。レビューではLiteral肥大の兆候を即検出します。

良くない実装例: ケース4(Literal未使用による入力バリデーション過多)

# bad_no_literal_validation.py

def process_response(status: str):
    if status not in ("SUCCESS", "FAIL", "TIMEOUT"):
        raise ValueError("Invalid status")
    print(f"処理: {status}")
@Reviewer
バリデーション処理が散在し型安全が崩壊しています。Literal型で定義し型安全に誘導してください。

問題点

  • バリデーションが呼び出し都度散在
  • 型安全ではなく実行時検証依存
  • コスト高・抜け漏れ誘発

改善例

# good_literal_type_safety.py

from typing import Literal

ResponseStatus = Literal["SUCCESS", "FAIL", "TIMEOUT"]

def process_response(status: ResponseStatus):
    print(f"処理: {status}")

Literal型定義によりバリデーションコスト削減が実現できます。レビューでは型安全保証可否を即読み取ります。

観点チェックリスト

まとめ

Literal型設計は「設計者の責務線引き能力」を映す極めて重要な型領域です。
レビューアーは常に

  • なぜLiteralか?
  • 状態分類か定数管理か?
  • 粒度肥大していないか?

を読み解き、マジックナンバーではなく「型による責務制御」が成立しているかを読み取ります。
Literalレビューは現場型設計教育の極めて実戦的な教材領域になります。