ミュータブルとイミュータブル

ミュータブルとイミュータブル

【Python】ミュータブルとイミュータブル

 Python には ミュータブル なオブジェクトと イミュータブル なオブジェクトがあります。リスト、ディクショナリ、set は要素を入れ替えたり削除したりできるので、ミュータブルなオブジェクトです。数値、文字列、タプルなどは一度定義してしまうと、自身を作り変えることができないのでイミュータブルなオブジェクトに分類されます。実際のコードで確認してみましょう。まずはリストの要素を置き換えてみます。

# PYTHON_MUTABLE

# In[1]

# 芳香族化合物のリスト
aroma = ["トルエン", "フェノール", "アニソール"]

# フェノールをニトロベンゼンに置き換えます
aroma[1] = "ニトロベンゼン"

print(aroma)
['トルエン', 'ニトロベンゼン', 'アニソール']

 次はタプルの要素を置き換えてみます。

# In[2]

# 芳香族化合物のリスト
aroma = ("トルエン", "フェノール", "アニソール")

# フェノールをニトロベンゼンに置き換えます
aroma[1] = "ニトロベンゼン"

print(aroma)
TypeError
 Traceback (most recent call last)
  in ()
       3 
       4 # フェノールをニトロベンゼンに置き換えます
 ----> 5 aroma[1] = "ニトロベンゼン"
       6 
       7 aroma

 TypeError: 'tuple' object does not support item assignment

 タプルの要素を別の要素に置き換えようとするとエラーが発生しました。tuple object は item(要素)の assignment(割り当て)ができないと警告されています。リストには並び順を変更したり、要素を追加・削除するようなメソッドが豊富に用意されていますが、タプルにはそのようなメソッドがありません。しかし、タプルと同じくイミュータブルなオブジェクトである文字列になると少し話がややこしくなってきます。たとえば次のようなメソッドを使った例です。

# In[3]

#ベンゾフェノンをベンゾニトリルに変えます
x = "benzophenone".replace("phenone","nitrile")

print(x)
'benzonitrile'

 replace は文字列に備わる「文字を置き換える」メソッドです。上のサンプルコードでは benzophenone の phenone を nitrile に置き換えて、benzonitrile という単語に作り変えてしまっています。こんなことができてしまうのに、どうして文字列はイミュータブルであるといえるのでしょうか? 実はこのメソッドは文字列そのものを変更しているわけではなく、もとの文字列とは別の新しい文字列(コピー)を作って返しているのです。この仕組みは次のように文字列をいったん変数に格納しておくと理解できます。

# In[4]

#ベンゾフェノンをベンゾニトリルに変えます
x = "benzophenone"
x.replace("phenone", "nitrile")

print(x)
'benzophenone'

 x.replace("phenone","nitrile") によって新しい文字列を生成していますが、変数 x の参照先のオブジェクトが変更されているわけではないので、最後の x というコードでは、もとの文字列 benzophenone を表示するだけです。しかし、このコードをほんの少し変えて、次のようにしてみると、また結果は違ってきます。

# In[5]

#ベンゾフェノンをベンゾニトリルに変えます
x = "benzophenone"
x = x.replace("phenone","nitrile")

print(x)
'benzonitrile'

 今度は benzonitrile という作り変えられたほうの文字列が返ってきました。これは x という変数が作り変えられたほうの文字列を参照するようにしているからです。細かい話のように思えますが、データの所在を常に頭の中で追跡する癖をつけておかないと、複雑なプログラムを作って不具合が生じたときに対処が難しくなります。
 
 リストのようなミュータブルなオブジェクトが、並び替えや要素の追加などによって自身を書き換える操作のことを「破壊的操作」とよぶことがあります。リストに備わる sortメソッドや、appendメソッドも破壊的操作を伴います (もちろん、ミュータブルなオブジェクトのメソッドすべてが破壊的操作を伴うわけではありません)。一方で、イミュータブルなオブジェクトには破壊的操作を伴うメソッドは1つもありません。