メインコンテンツへスキップ

dataclassのミュータブルとイミュータブルの違いと使い分け

🐍はじめに

ここでは、dataclassでの「ミュータブルとイミュータブルの違い」と、その使い分けの指針について解説していきます!


⚔️ ミュータブル vs イミュータブル:違いと比較

項目ミュータブル (`frozen=FalseFalse`, デフォルト)イミュータブル (`frozen=TrueTrue`)
属性の変更後から変更できる変更できない(エラーになる)
__hash__`__hash__`自動で付かない(辞書キーに使えない)自動で付く(辞書キーに使える)
バグ耐性変更による意図しない副作用が起きうる副作用がないため安全
関数型スタイルとの相性原則NG(変更が前提)非常に良い
共有時の安心感他の関数で書き換えられる可能性ありどこからも変更されない安心感
再利用性状態依存で再利用しづらいことも常に同じなので再利用しやすい

ミュータブルは「変化」を、イミュータブルは「安定性」を重視した選択になります。


🧭 使い分けの指針:どう選ぶ?

✅ ミュータブルを使うべき場面

  • 「状態」を持つオブジェクト(例:ゲームキャラクター、セッション情報など)

  • オブジェクトの一部の情報だけを頻繁に更新したいとき

  • 外部APIとのやりとりで「値を上書きして送る」ような場合

例:ユーザーが編集できる設定情報など

@dataclass
class Settings:
    theme: str
    font_size: int

ミュータブルなら `.font_size = 1616` のように変更OK!


✅ イミュータブルを使うべき場面

  • 一度決めたら変わらないもの(例:ユーザー登録情報、計算結果、識別子など)

  • **辞書のキーや集合(set)** に使いたいとき

  • 参照されるだけの安全な値を共有したいとき

  • テストしやすく保守性の高いコードを書きたいとき

例:ユーザーのプロフィール情報など(登録後は基本的に変更不可)

@dataclass(frozen=True)
class Profile:
    id: int
    username: str

「外から書き換えられたら困るデータ」はイミュータブルにすると事故防止になります!


🎓 補足:部分だけ凍らせたいときはどうする?

実は、dataclass全体を frozen=True にしなくても、内部にイミュータブルな型を使うことで安全性を高められます。

from typing import NamedTuple

class Address(NamedTuple):  # NamedTupleはイミュータブル
    city: str
    zip: str

@dataclass
class Customer: # Customer自身はfrozon=Trueがついていないのでミュータブル
    name: str
    address: Address  # イミュータブルな部分

こうすることで、「この部分だけは絶対変えない」という設計もできます!


🧩 最後に:変えられる vs 変えたくない

  • 変えたい時 → ミュータブル

  • 変えたくない時 → イミュータブル

というのは大前提ですが、それ以上に重要なのは、その意図がコードで明示されることです。

他人にとっても「なるほど、これは変えちゃダメな値なんだな」と一目で分かる設計は、読みやすさと信頼性の両方に繋がります!