Pythonのミュータブルとイミュータブルの違いについて

Pythonの勉強,イメージ

AI実装検定のご案内

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 によって再利用される

まとめ

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

以上、Pythonのミュータブルとイミュータブルの違いについてでした。

最後までお読みいただき、ありがとうございました。

よかったらシェアしてね!
  • URLをコピーしました!
  • URLをコピーしました!
目次