この記事では、Pythonのミュータブル(mutable)とイミュータブル(immutable)なオブジェクトについて解説します。
ミュータブルとは、値を変更できるオブジェクトのこと で、イミュータブルとは、値が変更できないオブジェクトのこと を言います。
それでは、もう少し詳しくミュータブルとイミュータブルについて見てきましょう!
ミュータブルとイミュータブルとは?
値を変更できるオブジェクトのことを「ミュータブル」と呼び、値が変更できないオブジェクトのことを「イミュータブル」と呼びます。
値が変更できるかどうかと言いましたが、ここで重要なのは「同じオブジェクトのまま値が変更できるかどうか」ということです。
int
型はイミュータブルなので同じオブジェクトのまま値を変更することができません。以下のコードでは、値を変更しているかに見えますが単に異なるオブジェクトを代入しているに過ぎない。
num = 1
num = 2
IDを比較することで同じオブジェクトなのか確認することができます。参考 → 「オブジェクトの識別値(ID)について」
num = 1
id1 = id(num)
num = 2
id2 = id(num)
print(id1 == id2)
実行結果
False
同じオブジェクトのまま値を変更するにはメソッドやプロパティを使えばいいですが、int
型はイミュータブルなのでそのようなメソッドは実装されていません。
プロパティから直接値を書き換えようとするとAttributeError
が発生します。
num = 1
# すべてAttributeError
num.real = 2
num.imag = 2
num.numerator = 2
num.denominator = 2
このように、同じオブジェクトのまま値が変更できないオブジェクトを「イミュータブル」と呼び、反対に変更できるオブジェクトを「ミュータブル」と呼びます。
リストなどのミュータブルなオブジェクトは、以下のように同じオブジェクトのまま値だけ変更することができます。
l = [1, 2]
id1 = id(l)
l.append(3)
id2 = id(l)
print(l) # [1, 2, 3]
print(id1 == id2) # True
ミュータブルとイミュータブルな型一覧
ミュータブルの型には、以下のようなものがあります。
- list
- dict
- set
- bytearray
- ユーザー定義クラス
イミュータブルの型には、以下のようなものがあります。
- bool
- int
- flaot
- complex
- str
- tuple
- range
- bytes
- file object
例外的なイミュータブル
イミュータブルな型は、同じオブジェクトのまま値を変更できないと言ってきましたが例外的なものもあります。
それは「ミュータブルな要素を持つイミュータブル」です。
例えば、タプルの中にリストが格納されている場合などです。
t = (1, 2, [])
identity = id(t) # 変更前のID
t[2].append(99)
print(t)
# (1, 2, [99])
# 変更後のIDと比較
print(identity == id(t))
# True
このように、ミュータブルな要素を持つイミュータブルなオブジェクトは、IDを変更せずとも値を書き換えることができます。
ミュータブルなオブジェクトの注意点
ミュータブルなオブジェクトは、他の変数に代入して使用される場合、元のオブジェクトにも影響を与えてしまいます。
l = []
# l を new_l に代入
new_l = l
# new_l に要素を追加
new_l.append(1)
# l に要素が追加されている
print(l) # [1]
関数の引数に渡す際も同様です。
l = []
def func(data: list):
data.append(1)
func(l)
print(l) # [1]
元のオブジェクトに影響を与えたくない場合はコピーを生成します。
まとめ
この記事では、ミュータブルとイミュータブルについて解説しました。
ミュータブルなオブジェクトを使用する場合には、元のオブジェクトに影響があるのかどうかを注意して使用しましょう。
それでは今回の内容はここまでです。ではまたどこかで〜( ・∀・)ノ