Pythonのオブジェクトには、mutableなオブジェクトとimmutableなオブジェクトがあり、それらの振る舞いの違いには注意が必要です。
mutableとimmutable
- mutable(ミュータブル)
値を変更することができるオブジェクトのこと
リストなどがこれにあたる。 - immutable(イミュータブル)
生成後に値を変更できないオブジェクトのこと
数値型、文字列型、タプル型のインスタンスがこれにあたる。
この定義は完全に正確なものではありません。
詳しくは、Pythonドキュメントのデータモデル項をご確認ください。
振る舞いの違い
mutableなオブジェクトの場合
mutableなオブジェクトの場合、たとえば、リストを別の変数にコピーしておいてから利用する場合には注意が必要です。
>>> array001 = [0, 1, 2, 3, 4, 5]
>>> temp_array001 = array001
>>>
>>> print(temp_array001)
[0, 1, 2, 3, 4, 5]
>>> temp_array001[3] = 10
>>> print(temp_array001)
[0, 1, 2, 10, 4, 5]
>>> print(array001)
[0, 1, 2, 10, 4, 5]
上の例では、リストarray001を作成後、変数temp_array001へ代入しています。
その後、temp_array001の3番目の要素を変更し、2つのリストの中身を表示してみると、もとのリストarray001の要素の値まで変更されていることが確認できると思います。
リストを関数の引数として渡した場合にも同様な振る舞いを確認できます。
このことは、変数に代入された値がリストの値ではなく、リストの値が保存された格納場所(メモリ上のアドレス)が代入されているために起こります。つまり、2つの変数は同じオブジェクトを参照しているために、どちらかの変数を利用してその値を書き換えてしまえば、どちらから見ても同じように変更された結果が得られるという動きになります。
リストの値を変更前と変更後で比較したい場合は、以下のようにdeepcopyを使用することで実現することができます。
>>> array001 = [0, 1, 2, 3, 4, 5]
>>> temp_array001 = array001
>>>
>>> print(temp_array001)
[0, 1, 2, 3, 4, 5]
>>> temp_array001[3] = 10
>>> print(temp_array001)
[0, 1, 2, 10, 4, 5]
>>> print(array001)
[0, 1, 2, 10, 4, 5]
immutableなオブジェクトの場合
上でみた振る舞いを今度は、immutableなオブジェクトで確認してみます。
>>> str001 = 'Hello World!'
>>> temp_str001 = str001
>>>
>>> print(str001)
Hello World!
>>> print(temp_str001)
Hello World!
>>>
>>> print(id(str001))
140061236888688
>>> print(id(temp_str001))
140061236888688
>>>
>>> temp_str001 = 'See you!'
>>> print(temp_str001)
See you!
>>> print(str001)
Hello World!
>>>
>>> print(id(str001))
140061236888688
>>> print(id(temp_str001))
140061236889072
先の例と同様に生成したimmutableな文字列オブジェクトstr001をtemp_str001に代入した後、
temp_str001の値を変更しています。
変更前後で両者の値を確認してみると、当然ですが、変更前は同じ値を表示していますが、変更後は、
temp_str001の値のみ変わり、str001の値はもとのままであることがわかります。
なぜ、このような違いが起こるのかを確認するために、こちらのプログラムではid関数でそれぞれの変数が指し占めるアドレスを表示しています。
わかりにくいですが、もともと同じアドレスを指していたものが、temp_str001の値を変更した後では、
str001がもとのアドレスと同じままなのに対し、temp_str001のアドレスが変わっていることがわかります。
immutableなオブジェクトの場合、値を変更したタイミングで別のアドレスを参照することになります。
これは、もともとのアドレスの値を書き換えているわけではなく、メモリ上の別の場所に新たなオブジェクトが生成され、そこのアドレスが代入されたことを意味しています。
コメント