■ 変数について
プログラム言語では、変数とは、なんらかの値を格納する領域です。Pythonの場合、使用する前に変数を宣言する必要はありません。変数に値を代入した時点で、領域が用意されます。
Pythonのようなオブジェクト指向言語では、変数に値そのものが格納されるのではなく、変数の値を指し示す「リファレンス」が格納されると考えるとよいでしょう。
>>> name1 = "山田一郎"
あるいは、「変数名」は値に設定されたタグのようなイメージでとらえてもかまいません。
次のように、変数を別の変数に代入した場合には、同じ値を指し示すようになります。
>>> name1 = "山田一郎"
>>> name2 = name1
■ id()関数でオブジェクトのIDを調べる
Pythonでは数値や文字列を含め、すべての値はオブジェクトです。オブジェクトのID(識別値)を調べるにはid()関数を使用します。変数名を引数にid()関数を実行すると参照しているオブジェクトのIDを表示します。前述の例で変数name1とname2のIDをid()関数で調べると、同じIDのオブジェクトを参照していることがわかります。
>>> name1 = "山田一郎"
>>> name2 = name1
>>> id(name1)
4339005200 ←IDが同じ
>>> id(name2)
4339005200 ←IDが同じ
■ 変数に値を再代入した場合
変数に別の値を再代入した場合にはどうでしょう?次の例では、まず、①で変数numに10を代入し、②で変数numの値に「1」を加えて、変数numに代入しています。
>>> num = 10 ←①
>>> num = num + 1 ←②
この場合、②で値が「11」の新たなオブジェクトが生成され、そのリファレンスが変数numに格納されます。つまり①と②では変数numは異なるオブジェクトを指し示しています。
このことはid()関数を使用して確かめられます。
>>> num = 10
>>> id(num)
4297538176
>>> num = num + 1
>>> id(num)
4297538208 ←IDが変化した(参照先が変更された)
■ ミュータブルな値とイミュータブルな値
Pythonのデータ型は、後から値を変更可能な「ミュータブル」な型と、変更出来ない「イミュータブル」に大別されます。
ミュータブル リスト、ディクショナリ、セット,....
イミュータブル 文字列、数値、タプル, ...
Pythonでは文字列や数値といった基本的なデータ型もイミュータブル、つまり後から変更出来ない型です。
さて、前述の例では変数numの値に1を足して、変数numに再代入していました。
>>> num = 10 ←①
>>> num = num + 1 ←②
これは一見、変数numの値を変更しているように見えるので、数値型はミュータブルな型なのでは?と思うかもしれませんが、実はそうではありません。id()関数の実行結果を見るとわかるように、①と②では、変数numは異なる値を指し示しているのです。
■ リストはミュータブル、タプルはイミュータブル
さて、Pythonでは「リスト」はミュータブル、つまり後から値を変更可能な型です。次の例では、3つの整数を要素とするリスト「ages」を生成し、3番目の要素を変更しています。
>>> ages = [1, 2, 3]
>>> ages[2] = 4
>>> ages
[1, 2, 4]
id()関数で調べると、当然のことながら要素の値を変更後もIDは変化していません。
>>> ages = [1, 2, 3]
>>> id(ages)
4338960072
>>> ages[2] = 4
>>> id(ages)
4338960072 ←IDは同じ
それに対してタプルはイミュータブルな型です。要素の値を変更しようとするとエラーになります。
>>> nums = (1, 2, 3)
>>> nums[0] = 0
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: 'tuple' object does not support item assignment
■ 文字列もイミュータブル
文字列もイミュータブルな型です。したがって、次のように内部の文字を変更しようとするとエラーになります。
>>> str = "abc"
>>> str[1] = "d" # 2文字めに「"d"」を代入
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: 'str' object does not support item assignment
誤解しやすい例として、次の例を見てみましょう。
>>> str = "abc"
>>> str = str + "bcd" # strと"bcd"を連結してstrに再代入 ←①
>>> str
'abcbcd'
この例では、①で変数strと文字列「"bcd"」を連結しているため、文字列が変更されたように見えますが、そうではありません。①で新たな文字列「"abcbcd"」が生成され、そのリファレンスが変数strに格納されるのです。
id()関数で調べると①の後でIDが変更されていることが確認できます。
>>> str = "abc"
>>> id(str)
4301952704
>>> str = str + "bcd"
>>> id(str)
4339056968 ←IDが変化した
>>> str
'abcbcd'