# イミュータブルってなに?

# 簡単に言えば...


変更できないオブジェクトのことをイミュータブルと言います。 反対に変更できるオブジェクトのことをミュータブルと言います。

値を変更できるオブジェクトのことを mutable と呼びます。
Objects whose value can change are said to be mutable;

値を変更できないオブジェクトのことを immutable と呼びます。
objects whose value is unchangeable ... are called immutable.

3.1. オブジェクト, 値 そして 型 - Python 言語リファレンス
3.1. Objects, values and types - The Python Language Reference

値という文字を薄くしておきました。 とりあえずいまは、変更できるオブジェクトは mutable, 変更できないオブジェクトは immutable と考えていただいて差し支えありません。

# ◯ mutable なオブジェクト

例えば、list 型、 dict 型、class 定義文で定義したクラスは mutable です。

# class 定義文で定義したクラス

class Person:
    def __init__(self, name):
        self.name = name

person = Person('yaruo')
person.name

# 変更できた -> mutable
person.name = 'yarumi' 
person.name  # 'yarumi'
>>> # 変更できた -> mutable
... person.name = 'yarumi' 
>>> person.name  # 'yarumi'
'yarumi'
>>> 

# list 型

lst = [1, 2, 3]

# 変更できた -> mutable
lst[2] = 4
lst  # [1, 2, 4]
>>> # 変更できた -> mutable
... lst[2] = 4
>>> lst  # [1, 2, 4]
[1, 2, 4]
>>> 

# ◯ immutable なオブジェクト

例えば、int, str, bool と tuple のインスタンスは immutable です。

# int 型

a = 1

# 1 の実部
a.real  # 1

# 1 の虚部
a.imag  # 0

# 変更できない -> immutable
i.imag = 100  # AttributeError
>>> # 変更できない -> immutable
... i.imag = 100  # AttributeError
Traceback (most recent call last):
  File "<stdin>", line 2, in <module>
NameError: name 'i' is not defined
>>> 

AttributeError
属性参照や代入が失敗した場合に送出されます (オブジェクトが属性の参照や属性の代入をまったくサポートしていない場合には TypeError が送出されます)。

# str 型

s = 'ランボー/怒りの脱出'

s[0]  # 'ラ'

# 変更できない -> immutable
s[0] = 'チ'  # TypeError
>>> # 変更できない -> immutable
... s[0] = 'チ'
Traceback (most recent call last):
  File "<stdin>", line 2, in <module>
TypeError: 'str' object does not support item assignment
>>> 

TypeError
組み込み演算または関数が適切でない型のオブジェクトに対して適用された際に送出されます。 関連値は型の不整合に関して詳細を述べた文字列です。

こう言うコード見せられると「やめろっ!!」って思いますよね。 まっ、エラーになるんですけど笑 あと正直、この時の AttributeErrot と TypeError の違いが、あまりよくわかっていません。

# ◯ immutable な型の一覧

以下に immutable な型を列挙します。

  • int
  • float
  • str
  • tuple
  • bool
  • range
  • type(None)

# ◯ よくある誤解

変数に代入できるから mutable だというのは誤りです。

a = 1
a = 2  # <- 変数 a に代入できたから int 型は mutable だよね!?

何故なら、変数に代入してもオブジェクトは変化しないからです。 反対に属性に代入できた場合は、オブジェクトが変化します。 変数への代入と属性への代入の違いについては、前回の記事で見てきました。


# 正確に言えば...

mutable属性に直接代入されている
オブジェクトを取り替えられる。
imutable属性に直接代入されている
オブジェクトを取り替えられない。


例えば tuple はオブジェクトを変更することができます。 ですが immutable に分類されます。

# tuple は immutable だけど...
person = ('サーバル', 17, ['かばん', 'ラッキービースト'])

# オブジェクトを変更できる -> だけど immutable
person[2].append('アライグマ')

person

>>> person
('サーバル', 17, ['かばん', 'ラッキービースト', 'アライグマ'])
>>> 

# ◯ 変更できる immutable なオブジェクト

mutable object への参照を持っている immutable container object は、値が変更できますが immutable です。

mutable object への参照を持っている immutable container object の値は、 参照している mutable object の値が変化させられた時に変化すると言えます。 しかしながら container (an immutable container object) は immutable であると判断されます、
The value of an immutable container object that contains a reference to a mutable object can change when the latter’s value is changed; however the container (an immutable container object) is still considered immutable,
3.1. Objects, values and types

なんで?どうして?

なぜなら container が所持しているオブジェクトの集合は変化していないからです。 従って immutable であること (immutability) は、厳密に言えば "値が変更できないこと" と同義ではなく、もう少し複雑です。
because the collection of objects it contains cannot be changed. So, immutability is not strictly the same as having an unchangeable value, it is more subtle.
3.1. Objects, values and types

# ◯ mutable object への参照を持っている
immutable container object ってなに?

答え: mutable なオブジェクトが属性に代入された immutable なオブジェクト

例えば、以下の変数person に代入されたオブジェクトがそうです。 一つ一つ見ていきたいと思います。

# tuple は immutable だけど...
person = ('サーバル', 17, ['かばん', 'ラッキービースト'])

# オブジェクトを変更できる -> だけど immutable
person[2].append('アライグマ')

person

# Step1. object

「変数に代入できるもの」は、全てオブジェクトだと理解しています。

a = 1
b = 'Hello, world!'
c = [1, 2, 3, 4]
d = (1, 2, 3, 4)
e = ['a':1, 'b':2, 'c':3]

# Step2. immutable object

int, strings, tuples は immutable です。

オブジェクトが mutable かどうかはその型によって決まります。 例えば、数値型(int, float などの総称か)、 文字列型とタプル型のインスタンスは immutable で、dict や list は mutable です。
An object’s mutability is determined by its type; for instance, numbers, strings and tuples are immutable, while dictionaries and lists are mutable.
3.1. Objects, values and types - The Python Language Reference

# Step3. container object

ほぼほぼ全てのオブジェクトが複数の属性を持っているので、 ほぼほぼ全てのオブジェクトがcontainer オブジェクトだって認識でいいのではないでしょうか... int も複数の値を持ってますしね。

container - Python 言語リファレンス 他のオブジェクトに対する参照をもつオブジェクトもあります; これらは コンテナ (container) と呼ばれます。 コンテナオブジェクトの例として、タプル、リスト、および辞書が挙げられます。オブジェクトへの参照自体がコンテナの値の一部です。 — ワイの注記 container について記述されている箇所の抜粋しました。 タプル、リスト、および辞書など集合を表現するオブジェクトを container だと言いたい様子。 ただ、この定義だと全てのオブジェクトが container に該当してしまうんじゃまいか..

コンテナ (データ型) - Wikipedia コンテナとはオブジェクトの集まりを表現するデータ構造、抽象データ型またはクラスの総称である。

# Step4. immutable container object

Step2, 3 を踏まえると... int, str, tuple は immutable container object と言えそうですね。

# Step5. mutable object への参照を持っている immutable container object

答え: mutable なオブジェクトが属性に代入された immutable なオブジェクト

タプルがそれに該当します。 さっそく変更できる immutable なオブジェクトを見ていきたいと思います。

# list である friends_list は mutable です。
friends_list = ['かばん', 'ラッキービースト']


# tuple である person は immutable です。
person = ('サーバル', 17, friends_list)

# そのため直下のオブジェクトは変更できません。
person[0] = ['ワイ']
>>> person[0] = ['ワイ']
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: 'tuple' object does not support item assignment
>>> 

しかし タプルの要素 person[2] に代入されたリストは mutable なので 値を別のオブジェクトに変更できます。

# list である friends_list は mutable です。
friends_list = ['かばん', 'ラッキービースト']

# tuple である person は immutable です。
person = ('サーバル', 17, friends_list)

# オブジェクトを変更できる -> だけど immutable
person[2].append('アライグマ')

person
>>> person
('サーバル', 17, ['かばん', 'ラッキービースト', 'アライグマ'])
>>> 

# ◯ まとめ

変更できないオブジェクトをイミュータブルと呼びます。 ただし、属性の属性、あるいは要素の要素は変更できても、イミュータブルと呼んで良いです。

mutable属性に直接代入されている
オブジェクトを取り替えられる。
imutable属性に直接代入されている
オブジェクトを取り替えられない。

# なんでイミュータブルは重要なの?

つぎは、なぜこんな、たかだか変更できないというだけのことに、 immutable という名前までつけて有難がっているのかについて考えていきます。 「副作用」という言葉がキーワードになります。