dataclassのミュータブルとイミュータブルの違いと使い分け
🐍はじめに
ここでは、dataclass
での「ミュータブルとイミュータブルの違い」と、その使い分けの指針について解説していきます!
⚔️ ミュータブル vs イミュータブル:違いと比較
項目 | ミュータブル (frozen=False , デフォルト) |
イミュータブル (frozen=True ) |
---|---|---|
属性の変更 | 後から変更できる | 変更できない(エラーになる) |
__hash__ |
自動で付かない(辞書キーに使えない) | 自動で付く(辞書キーに使える) |
バグ耐性 | 変更による意図しない副作用が起きうる | 副作用がないため安全 |
関数型スタイルとの相性 | 原則NG(変更が前提) | 非常に良い |
共有時の安心感 | 他の関数で書き換えられる可能性あり | どこからも変更されない安心感 |
再利用性 | 状態依存で再利用しづらいことも | 常に同じなので再利用しやすい |
ミュータブルは「変化」を、イミュータブルは「安定性」を重視した選択になります。
🧭 使い分けの指針:どう選ぶ?
✅ ミュータブルを使うべき場面
-
「状態」を持つオブジェクト(例:ゲームキャラクター、セッション情報など)
-
オブジェクトの一部の情報だけを頻繁に更新したいとき
-
外部APIとのやりとりで「値を上書きして送る」ような場合
例:ユーザーが編集できる設定情報など
@dataclass
class Settings:
theme: str
font_size: int
ミュータブルなら .font_size = 16
のように変更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 変えたくない
-
変えたい時 → ミュータブル
-
変えたくない時 → イミュータブル
というのは大前提ですが、それ以上に重要なのは、その意図がコードで明示されることです。
他人にとっても「なるほど、これは変えちゃダメな値なんだな」と一目で分かる設計は、読みやすさと信頼性の両方に繋がります!