この記事のポイント

  • __post_init__の責務設計をレビューで整理できる
  • 初期化後ロジックの肥大化パターンを見抜ける
  • 構造的に安全な責務分離設計技法を学べる

そもそも__post_init__とは

Pythonのdataclassでは、__init__は自動生成されます。
追加の初期化処理が必要な場合、__post_init__メソッドを定義できます。

from dataclasses import dataclass

@dataclass
class User:
    name: str
    age: int

    def __post_init__(self):
        if self.age < 0:
            raise ValueError("年齢は0以上でなければなりません")

特徴は以下です。

  • __init__実行後に自動呼び出し
  • 初期化検証・補完処理向き
  • 可読性と責務整理を意識しないと崩壊しやすい

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

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

  • __post_init__が肥大化し過ぎる
  • バリデーションと依存初期化が混在
  • 外部I/O・副作用処理が混入
  • 可読性・テスト性・保守性崩壊

レビューアーは「これは本当に初期化責務か?」を常に読み取ります。

レビューアー視点

  • __post_init__の責務範囲が整理されているか
  • バリデーション・依存初期化・副作用処理が分離されているか
  • 外部アクセスが混入していないか
  • テスト可能性が維持されているか
  • データクラス本体の責務肥大化が発生していないか

開発者視点

  • __post_init__は「データ整合性検証」に限定
  • 外部依存初期化はファクトリ・サービス層へ分離
  • 複雑ロジックは専用メソッドへ抽出
  • 単一責務原則(SRP)を強く意識
  • 無理な状態自動修正を行わない

良い実装例

なぜこの実装が良いのか

  • __post_init__責務は整合性検証のみ
  • 外部依存は完全に分離
  • データ構造の単一責務性が明確
  • テスト性・再利用性が高い
# user.py

from dataclasses import dataclass

@dataclass
class User:
    name: str
    age: int

    def __post_init__(self):
        if not self.name:
            raise ValueError("名前は必須です")
        if self.age < 0:
            raise ValueError("年齢は0以上でなければなりません")
補足

「純粋な入力値整合性チェックのみ」に限定するのが安定設計です。レビューアーは副作用有無を即座に確認します。

レビュー観点

  • __post_init__がバリデーション専用になっているか
  • 外部依存処理が混入していないか
  • 複雑ロジックが専用関数化されているか
  • 自動補正・補完が暴走していないか
  • 責務が他クラスへ分離可能になっているか

良くない実装例: ケース1(外部依存初期化混入)

# bad_external_dependency.py

from dataclasses import dataclass

@dataclass
class Report:
    file_path: str

    def __post_init__(self):
        self.content = open(self.file_path).read()
@Reviewer
__post_init__で外部ファイル依存処理が混入しています。依存注入かファクトリ分離を検討してください。

問題点

  • I/O処理がデータ構造初期化に混入
  • テスト困難
  • 保守性崩壊

改善例

# good_external_injection.py

@dataclass
class Report:
    file_path: str
    content: str
# 別責務層で注入処理
def load_report(file_path: str) -> Report:
    content = open(file_path).read()
    return Report(file_path=file_path, content=content)

外部依存は明示注入原則。レビューではファイル・DBアクセス混入を即指摘します。

良くない実装例: ケース2(データ補完処理の混入)

# bad_auto_correction.py

from dataclasses import dataclass

@dataclass
class Config:
    timeout: int

    def __post_init__(self):
        if self.timeout < 0:
            self.timeout = 0
@Reviewer
自動補完による状態修正が混入しています。例外化または事前補正へ分離検討してください。

問題点

  • サイレント修正による不具合誘発
  • 入力値不整合が隠蔽される
  • バグ診断困難化

改善例

# good_validation_only.py

@dataclass
class Config:
    timeout: int

    def __post_init__(self):
        if self.timeout < 0:
            raise ValueError("timeoutは0以上である必要があります")

サイレント補完ではなくバリデーション例外設計が原則。レビューでは補完動作有無を確認します。

良くない実装例: ケース3(重複ロジック肥大)

# bad_business_logic_mixed.py

from dataclasses import dataclass

@dataclass
class Order:
    quantity: int
    unit_price: int

    def __post_init__(self):
        self.total_price = self.quantity * self.unit_price
@Reviewer
ビジネス計算ロジックが__post_init__に混入しています。専用計算責務に分離してください。

問題点

  • __post_init__に計算責務混入
  • 単一責務崩壊
  • 再計算・再利用困難化

改善例

# good_separate_calculation.py

@dataclass
class Order:
    quantity: int
    unit_price: int

    def total_price(self) -> int:
        return self.quantity * self.unit_price

計算責務は専用関数へ分離が設計原則。レビューでは計算混入有無を重点確認します。

良くない実装例: ケース4(複合責務肥大)

# bad_mixed_responsibilities.py

from dataclasses import dataclass

@dataclass
class Account:
    user_id: str
    balance: int
    overdraft_allowed: bool

    def __post_init__(self):
        if not self.user_id:
            raise ValueError("user_id必須")
        if self.balance < 0 and not self.overdraft_allowed:
            raise ValueError("残高不足")
        self.is_active = self.balance >= 0
@Reviewer
検証・規約・状態派生処理が混在しています。責務ごとに整理分離してください。

問題点

  • バリデーション・ビジネス規約・状態派生が混在
  • 読み手負荷増大
  • 保守困難化

改善例

# good_responsibility_split.py

@dataclass
class Account:
    user_id: str
    balance: int
    overdraft_allowed: bool

    def __post_init__(self):
        if not self.user_id:
            raise ValueError("user_id必須")
        if self.balance < 0 and not self.overdraft_allowed:
            raise ValueError("残高不足")

    def is_active(self) -> bool:
        return self.balance >= 0

純粋バリデーションと派生状態計算は分離が設計安定策。レビューでは肥大混在を即検出します。

観点チェックリスト

まとめ

__post_init__レビューは「初期化後ロジックの責務線引き訓練」です。
レビューアーは常に

  • これは純粋な整合性検証か?
  • 外部依存を持っていないか?
  • 派生処理が混入していないか?

を読み取り、安全で明確な初期化設計を導くレビュー習慣を形成します。
__post_init__責務整理は現場設計育成教材として非常に有効です。