Pythonには、ミュータブルとイミュータブルという概念があります。
この概念を理解することで浅いコピー(shallow copy),深いコピー(deep copy)がとても理解できるようになります。
ミュータブルとイミュータブルの意味
日本語でミュータブルは可変、イミュータブルは不変という意味です。
Pythonのみならず他の言語でも同じような概念があります。
一言でいうと、ミュータブルは、変更可能なオブジェクトでイミュータブルは、変更不可能なオブジェクトです。
オブジェクトとは
プログラミング言語には、様々なデータ型が存在します。
文字列型(str)、整数型(int)、浮動小数点(float)、リスト(list)、タプル(tuple)、etc….
このデータ型を入れる箱をオブジェクトといいます。
データ型とミュータブル、イミュータブルの対応
それぞれのデータ型がミュータブル、イミュータブルどれに対応しているのかの表にしました。
データ型 | ミュータブル | イミュータブル |
整数型 | 〇 | |
浮動小数型 | 〇 | |
文字列型 | 〇 | |
リスト型 | 〇 | |
辞書型 | 〇 |
疑問
ミュータブルは可変、イミュータブルは不変することは分かりましたが、
整数型(int)、文字列型(str)は、イミュータブルでありながら、変更することができると思いますよね。
svar = "Hello Python"
ivar = 100
fvar = 100.0
svar = "Hello World"
ivar = 1
fvar = 1.0
このようにイミュータブル型である整数型、文字列型、浮動小数点型である変数が変更できました。
可変、不変の意味
しかし、ここでいう変更は、オブジェクト内で変更できるか否かを示しています。
オブジェクトは、目には見えにくいのでidで確認しましょう。
id関数で確認できます。
svar = "Hello Python"
ivar = 100
fvar = 100.0
p_sid=id(svar);p_iid=id(ivar);p_fid=id(fvar)
svar = "Hello World"
ivar = 1
fvar = 1.0
n_sid=id(svar);n_iid=id(ivar);n_fid=id(fvar)
print("str :","%20d,%20d" % (p_sid,n_sid))
print("int :","%20d,%20d" % (p_iid,n_iid))
print("float :","%20d,%20d" % (p_fid,n_fid))
実行例
str : 140446729280240, 140446728878256
int : 94054698031520, 94054698028352
float : 140446729291984, 140446746212176
変更前と変更後でidが変わったことが分かったでしょうか。
9~11行目は、ごちゃごちゃしていますが、idを見やすくするため書式を変えただけです。
つまり、新しいオブジェクトで再定義したため、イミュータブルであっても変更できたのです。
オブジェクトが違えば、イミュータブル、ミュータブル関係なしに変更できます。
つぎに、今のオブジェクトのままで値を変えてみたいと思います。
値だけ変更する場合、スライスを用います。
svar = "Hello Python"
ivar = 100
fvar = 100.0
svar[:] = "Hello World"
ivar[:] = 1
fvar[:] = 1.0
実行例
File "sample.py", line 5, in <module>
svar[:] = "Hello World"
TypeError: 'str' object does not support item assignment
このように、変更できません。
文字列型でエラーメッセージが出ていますが、他の整数型、浮動小数点型でも同様のエラーメッセージが出ます。
逆にミュータブルであるリストをオブジェクトを変えずに変更したいと思います。
lvar = [1,2,3,4,5]
p_lid = id(lvar)
lvar[:] = [1,2,3]
n_lid = id(lvar)
print("list:","%20d,%20d" % (p_lid,n_lid))
実行例
list: 139969588189632, 139969588189632
idに変化はありません。
つまり、オブジェクトを変えることなく値を変更することができました。
まとめ
ミュータブルは同じオブジェクトで変更可能で、イミュータブルは同じオブジェクトで変更不可能。
同じ変数でも変数=値とすると、新しいオブジェクトが生成されるためミュータブル、イミュータブル関係なしに変更できる。
オブジェクトを変えず、値を変更したい場合、スライスを用います。
オブジェクトを変えず、値を変更したい場合、ミュータブルは変更でき、イミュータブルは変更できません。