Python で mutable と immutable の違い


f:id:domodomodomo:20161101225023p:plain

煽らないでください。

簡単に言えば...

を変更できるオブジェクトのことを mutable と呼びます。

生成後にを変更できないオブジェクトのことを immutable と呼びます。

3. Data model — Python 3.5.3 documentation

では、正確に言えば... どうなるのでしょうか?順を追って見て行きましょう。

○ mutable なオブジェクトの例

例えば、list 型、 dict 型、ユーザが定義した型は mutable です。

>>> class Person():
...   def __init__(self, name):
...     self.name = name
... 
>>> 
>>> person = Person('yaruo')
>>> 
>>> person.name
'yaruo'
>>> 
>>> # value 値を変更できた -> mutable
>>> person.name = 'yarumi' 
>>> person.name
'yarumi'

○ immutable なオブジェクトの例

例えば、数値型、文字列型とタプル型のインスタンスは immutable です。

>>> t = ('yaruo', 'yaranaio')
>>> 
>>> t
('yaruo', 'yaranaio')
>>>
>>> t[0]
'yaruo'
>>> 
>>> # value 値を変更できない -> immutable
>>> t[0] = 'yarumi'
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: 'tuple' object does not support item assignment


immutable なオブジェクトを自作するには一手間いるので、とりあえず数値型、文字列型とタプル型あたりは immutable だと抑えておけばいいのではないでしょうか。

immutable なオブジェクトをインスタンス化するクラスの一覧とかあったり、わかりやすいと思うんですけどね。

オブジェクトが mutable かどうかはその型によって決まります。例えば、数値型、文字列型とタプル型のインスタンスは 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. Data model — Python 3.5.3 documentation

 

値 value とは

属性 attribute に代入されているインスタンスオブジェクトのことを指していると "思われます" 。
 

この辺りの用語的な定義やら何やらを探してますが、まだそれと思しきものを見つけられないでいます...。

ただ、色々読んでると Python で言う値 value ってのは、上記のような理解に落ち着くのかなと思っています。

でも、そう考えると、オブジェクトと値の関係も、再帰的に定義されてそうな気配がありますね。

object には複数の attribute がついていて
attribute には 1 つの value(object) が結びついてる的な...
object -*-> attribute -1-> value(object)

 

属性 attribute に代入される値 value を変更できないものを immutable って言う理解でいいのかな...?



もう少し詳しく...

さらにマニュアルの文言を見てみましょう。

従って immutable であること (immutability) は、厳密に言えば "値が変更できないこと" と同義ではなく、もう少し複雑です。

So, immutability is not strictly the same as having an unchangeable value, it is more subtle.

3. Data model — Python 3.5.3 documentation

 

え?違うの?immmutable なオブジェクトでも値を変更できるそうです。それはどのようなオブジェクトでしょうか?さらにさらにマニュアルの文言を見てみましょう。

mutable object への参照を持っている immutable container object の値は、参照している mutable object の値が変化させられた時に変化すると言えます。しかしながら container は 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 is still considered immutable,


なぜなら 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. Data model — Python 3.5.3 documentation

 

immmutable なオブジェクトでも値を変更できる具体例として mutable object への参照を持っている immutable container object というのが出てきました。

簡単に言えば "mutable な object が属性に代入された imutable なオブジェクト" です。

例えば "list オブジェクトが属性に代入された tuple オブジェクト" がそれに該当します。

([1, 2, 3], [4, 5], [6, 7, 8, 9]) みたいなオブジェクトです。

"mutable object への参照を持っている immutable container object" とは何か?

これは一体何やねん。一つ一つ見ていきたいと思います。

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

numbers, strings, tuples は immutable です。

オブジェクトが mutable かどうかはその型によって決まります。例えば、数値型、文字列型とタプル型のインスタンスは 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. Data model — Python 3.5.3 documentation

Step3. container object

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

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

コンテナ (データ型) - Wikipedia

Step4. immutable container object

Step2, 3 を踏まえると...
numbers, strings, tuples は immutable container object と言えそうですね。

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

タプル t がそれに該当します。さっそく値を変更できる immutable なオブジェクトを見てみましょう。

>>> 
>>> #
>>> # a, b, c は mutable
>>> #
>>>
>>> class Obj():
...   def __init__(self, attr):
...     self.attr = attr
... 
>>> 
>>> a = Obj('nihao')
>>> b = Obj('hello')
>>> c = Obj('hola')
>>> 
>>>
>>> #
>>> # tuple t は immutable
>>> #
>>>
>>> #
>>> # mutable なオブジェクト a, b, c への参照を持つ
>>> # immutable なオブジェクト t
>>> # 
>>>
>>> t = (a, b, c)
>>>
>>>
>>> #
>>> # t はタプル immutable なので
>>> # 値を別のオブジェクトに変更できない。
>>> #
>>>
>>> d = Obj('konnichiwa')
>>>
>>> t[2] = d
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: 'tuple' object does not support item assignment
>>> 
>>>
>>>
>>> #
>>> # でも タプル t の要素 t[2], c は mutable なので
>>> # 値を別のオブジェクトに変更できる。
>>> #
>>>
>>> t[2].attr
'hola'
>>> 
>>> t[2].attr = "konnichiwa"
>>> 
>>> t[2].attr
'konnichiwa'
>>> 

正確に言えば...

mutable ... 値(オブジェクト)の集合を変更できる
immutable ... 値(オブジェクト)の集合を変更できない
 

しかしながら container (an immutable container object) は immutable であると判断されます、なぜなら container が所持しているオブジェクトの集合は変化していないからです。

however the container (an immutable container object) is still considered immutable, because the collection of objects it contains cannot be changed.

3. Data model — Python 3.5.3 documentation

immutable なオブジェクトを生成するクラスを自作

こんなことできるんですね。

○ namedtuple 関数

namedtuple 関数を用いて immutable なオブジェクトを生成するクラスを作ることができます。


インスタンスオブジェクトの動作確認

>>> import collections
>>>
>>> # immutable なクラスの定義
>>> Point = collections.namedtuple('Point', ['x', 'y'])
>>>
>>> p = Point(11, y=22)
>>> 
Point(x=11, y=22)
>>> 
>>> # 1) 属性の参照ができる
>>> p.x
11
>>> 
>>> # 2) 属性の変更はできない
>>> p.x = 33
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: can t set attribute
>>>
>>>
>>> # 3) 属性の追加はできない
>>> p.z = 44
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'Point' object has no attribute 'z'



クラスオブジェクトの動作確認

>>> import inspect 
>>> 
>>> # 4) Point はクラスオブジェクト
>>> inspect.isclass(Point)
True
>>>
>>> # 5) tuple を継承したクラスで実現されている 
>>> Point.__bases__[0]
<class 'tuple'>
>>>
>>> 
>>> # 6) クラスオブジェクトへの関数の追加ができる
>>> #    区別 クラスオブジェクト Point <-> インスタンスオブジェクト p
>>>
>>> def add(self):
...   return self.x + self.y
... 
>>>
>>> Point.add = add
>>>
>>> # 7) メソッドとして利用できる。
>>> p.add()
33

引用先では、さらにでコレータにして簡易に immutable なオブジェクトを生成するクラスを自作できるようにしています。
Python で immutable な値クラスを作るデコレータ

○ Cpython 拡張

CPython なら Python そのものを拡張して immutable なオブジェクトのクラスを生成するクラスを作ることもできる様です。
How to make an immutable object in Python? - Stack Overflow