Pythonでは、オブジェクトの「変更可能性」によって、データ型は大きく2つに分類されます。
それが「ミュータブル(mutable)」と「イミュータブル(immutable)」です。
この区別は、コードの動作やバグの原因、パフォーマンス、メモリ効率に直結する重要な概念です。
目次
ミュータブル vs イミュータブル:定義と違い
区分 | 説明 | 代表的な型 |
---|---|---|
ミュータブル | オブジェクトの内部状態(値)を変更可能 | list , dict , set , bytearray など |
イミュータブル | オブジェクトの内部状態を変更不可能 | int , float , str , tuple , bool , frozenset , bytes など |
ミュータブルの特徴と挙動
中身を直接変更できる
a = [1, 2, 3]
a[0] = 99
print(a) # [99, 2, 3]
オブジェクトIDが変わらない(同じオブジェクトのまま)
print(id(a)) # 例: 139945678123456
a.append(4)
print(id(a)) # 同じID → 同じオブジェクトが使われている
関数での副作用が発生しやすい
def modify(lst):
lst.append(100)
data = [1, 2, 3]
modify(data)
print(data) # [1, 2, 3, 100]
元のオブジェクトが直接変更されるため、思わぬバグにつながることもあります。
イミュータブルの特徴と挙動
値の変更ができない(実際は新しいオブジェクトが生成される)
s = "hello"
s2 = s.replace("h", "H")
print(s) # "hello"(変更されていない)
print(s2) # "Hello"(新しい文字列)
代入や演算でIDが変わる
x = 10
print(id(x)) # 例: 140727123456000
x += 1
print(id(x)) # 違うID → 新しいオブジェクトが生成された
関数に渡しても元の値は変化しない(副作用がない)
def add_one(n):
return n + 1
num = 5
add_one(num)
print(num) # 5(変化なし)
tuple
の注意点:不変だけど中に可変オブジェクトがあると?
t = ([1, 2], 3)
t[0].append(99)
print(t) # ([1, 2, 99], 3)
tuple
自体の「構造」(要素の順序と参照先)は不変- ただし、参照先のオブジェクトがミュータブルならその中身は変更可能
よくある混乱:+=
の挙動
ミュータブル型(例:リスト)の場合
a = [1, 2]
print(id(a))
a += [3]
print(id(a)) # IDは同じ → オブジェクトがそのまま更新される
イミュータブル型(例:文字列、タプル)の場合
s = "abc"
print(id(s))
s += "d"
print(id(s)) # IDは変わる → 新しい文字列が生成されている
+=
はオブジェクトの型によって動作が異なる点に注意。
なぜこの違いが重要なのか?
副作用の管理
- ミュータブルを共有参照すると、思わぬ変更が生じる可能性がある
- イミュータブルは安全に関数や他の構造に渡せる
パフォーマンスと最適化
- イミュータブルオブジェクトは内部的にキャッシュされる場合があり、効率的
- 例:小さな整数や短い文字列は Python によって再利用される
まとめ

観点 | ミュータブル | イミュータブル |
---|---|---|
値の変更 | 可能 | 不可能(新しいオブジェクトで代替) |
ID(メモリ参照) | 変更されない | 操作のたびに新しくなることが多い |
関数での副作用 | 起きやすい(オブジェクト共有時) | 起きにくい(新しいオブジェクトが生成) |
使用例 | データの状態が変化する場面 | 定数や辞書キー、集合の要素など |
ハッシュ可能性 | 通常不可 | 通常可能(__hash__() がある) |
以上、Pythonのミュータブルとイミュータブルの違いについてでした。
最後までお読みいただき、ありがとうございました。