この記事のポイント

  • namedtupleとdataclassの選択基準をレビューできる
  • 不変性・構造安定性・責務整理の判断基準を明確にできる
  • 現場で頻発する構造設計の崩壊ポイントを読み解ける

そもそもnamedtupleとdataclassとは

namedtupleとは

from collections import namedtuple

Point = namedtuple("Point", ["x", "y"])
p = Point(1, 2)
  • 軽量不変オブジェクト
  • メモリ効率が高い
  • タプル互換(インデックスアクセスも可能)
  • イミュータブル(フィールド変更不可)

dataclassとは

from dataclasses import dataclass

@dataclass
class Point:
    x: int
    y: int
  • 柔軟な属性定義
  • デフォルトミュータブル(不変化も可能)
  • 比較演算・repr等自動生成
  • 実務上はこちらが主流

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

現場では以下のような失敗が頻発します。

  • namedtupleの汎用化誤用
  • dataclassの責務肥大化
  • 不変性設計の放置
  • 構造更新有無が明確化されていない
  • API契約の曖昧化

レビューアーは「データの流れと変更責務」を設計視点で読み取る必要があります。

レビューアー視点

  • 変更責務の有無が整理されているか
  • API契約と構造型が一致しているか
  • 構造更新有無を意識した型選択になっているか
  • namedtupleが適切に限定利用されているか
  • dataclassでの不変性検討が行われているか

開発者視点

  • API入力は不変型優先(namedtuple/dataclass(frozen=True))
  • 内部状態保持はdataclass柔軟利用
  • namedtupleは軽量用途に限定
  • 更新可能性と設計責務を型で表現
  • 再利用性・変更容易性を型構造に反映

良い実装例

なぜこの実装が良いのか

  • API入出力に不変型を適用
  • 業務内部構造にdataclass柔軟適用
  • 変更責務を設計意図通りに型選択
  • namedtupleは構造軽量化に限定使用
# api_dto.py

from collections import namedtuple

ApiRequest = namedtuple("ApiRequest", ["request_id", "endpoint", "client_ip"])
# domain_model.py

from dataclasses import dataclass

@dataclass
class ApiRequestLog:
    request_id: int
    endpoint: str
    client_ip: str
    archived: bool = False
補足

「API層は不変構造」「内部処理は柔軟構造」を型選択に反映しています。レビューアーはこの責務線引きを読む必要があります。

レビュー観点

  • API契約で構造変更有無が明示できているか
  • namedtuple利用がAPI入出力等に限定されているか
  • dataclass利用が状態保持用途で整理されているか
  • frozen指定含め不変性設計が検討されているか
  • 責務過剰化していないか

良くない実装例: ケース1(namedtuple汎用化の誤用)

# bad_namedtuple_overuse.py

from collections import namedtuple

ApiRequestLog = namedtuple("ApiRequestLog", ["request_id", "endpoint", "client_ip", "archived"])
@Reviewer
業務内部状態管理でnamedtupleを利用しています。状態保持用途ではdataclassを優先してください。

問題点

  • 業務層での状態更新が困難
  • 可読性・拡張性低下
  • 不変性用途逸脱

改善例

# good_namedtuple_scope.py

from dataclasses import dataclass

@dataclass
class ApiRequestLog:
    request_id: int
    endpoint: str
    client_ip: str
    archived: bool = False

namedtupleは「構造軽量API DTO用途」に限定が原則です。レビューでは汎用利用の有無を即確認します。

良くない実装例: ケース2(dataclassの不変性検討漏れ)

# bad_mutable_dto.py

from dataclasses import dataclass

@dataclass
class ApiRequest:
    request_id: int
    endpoint: str
    client_ip: str
@Reviewer
API入力用DTOに可変dataclassを利用しています。不変性を検討してください。

問題点

  • API入出力に可変型投入
  • バグ混入経路
  • 意図しない状態変更リスク発生

改善例

# good_frozen_dataclass_dto.py

from dataclasses import dataclass

@dataclass(frozen=True)
class ApiRequest:
    request_id: int
    endpoint: str
    client_ip: str

API契約=不変型が原則。frozen指定はレビューで優先確認対象です。

良くない実装例: ケース3(namedtupleとdataclassの混在不統一)

# bad_mixed_model.py

from dataclasses import dataclass
from collections import namedtuple

ApiRequest = namedtuple("ApiRequest", ["request_id", "endpoint", "client_ip"])

@dataclass
class ApiResponse:
    code: int
    message: str
@Reviewer
同一レイヤー内で型表現が混在しています。責務別に統一整理してください。

問題点

  • 同一設計層で型表現混在
  • 読み手認知負荷上昇
  • 実装統一性低下

改善例

# good_consistent_model.py

@dataclass(frozen=True)
class ApiRequest:
    request_id: int
    endpoint: str
    client_ip: str

@dataclass(frozen=True)
class ApiResponse:
    code: int
    message: str

層単位で型表現統一を徹底するのが設計原則。レビューでは一貫性崩壊を常に監視します。

良くない実装例: ケース4(namedtupleに可変要求)

# bad_namedtuple_mutation_attempt.py

from collections import namedtuple

Point = namedtuple("Point", ["x", "y"])
p = Point(1, 2)
p.x = 3
@Reviewer
namedtupleは不変型です。可変操作の要求設計はdataclassへ整理してください。

問題点

  • 可変型要求と不変型選択が不一致
  • 実行時例外混入
  • 保守性崩壊

改善例

# good_mutable_state.py

from dataclasses import dataclass

@dataclass
class Point:
    x: int
    y: int

更新責務が存在する構造はdataclassを自然選択。レビューでは更新責務の有無確認が重要です。

観点チェックリスト

まとめ

namedtupleとdataclassの設計レビューは「変更責務と構造責務の可視化訓練」です。
レビューアーは常に

  • 変更される構造なのか?
  • API契約と構造型が一致しているか?
  • 一貫した型表現が選択されているか?

を読み取り、「型定義が設計意図そのものを映す状態」に整備させていくレビュー力を養います。
この領域は現場設計者育成教材として非常に有効です。