Pythonの変数は値を格納する箱ではない!オブジェクト参照の本質を徹底解説

Pythonにおける変数の本質とは?

変数は「箱」ではない

多くのプログラミング入門書では、変数は「値を格納する箱」であると説明されています。しかし、Pythonではこれは正しくありません。Pythonにおいて変数は、オブジェクトを参照する「名前」に過ぎません。言い換えると、変数は「ラベル」や「タグ」のようなものであり、実際に値が格納されているのはオブジェクトです。

オブジェクトと参照の関係

Pythonでは、すべてのデータはオブジェクトとしてメモリ上に存在します。変数は、そのオブジェクトの場所(メモリアドレス)を参照しているに過ぎません。この参照の仕組みにより、変数は値そのものを保持するのではなく、値が存在する場所を指し示しているだけです。

実際の例で理解する

a = 10
b = a

上記のコードでは、a10 が割り当てられ、その後 ba が代入されています。しかし、ここで ba のコピーではなく、同じオブジェクト(10)を参照しています。つまり、ab は同じ場所を指しています。

print(id(a))  # 例えば、12345678
print(id(b))  # 同じアドレスを出力

id() 関数は、オブジェクトのメモリアドレスを返します。この結果から、ab が同一のオブジェクトを参照していることが確認できます。

Pythonの変数は「名前の付いたポインタ」

C言語やJavaとの違い

C言語やJavaでは、変数はメモリ上の場所に直接値を格納する「箱」のように扱われます。しかし、Pythonでは変数はポインタのような役割を果たします。
C言語では、変数はメモリ内の特定の場所に固定され、そこに値が保存されます。一方、Pythonでは、変数は**オブジェクトが存在する場所を指し示す「名前」**に過ぎません。

参照カウントとガベージコレクション

Pythonは参照カウントという仕組みでオブジェクトのメモリ管理を行います。オブジェクトが参照されている限りメモリ上に存在し、参照がなくなったときにガベージコレクションによって自動的に削除されます。

x = 100
y = x
x = 200

この場合、最初に x100 を参照し、次に y が同じ 100 を参照します。しかし、x200 に変更されると、y は依然として 100 を参照しています。ここで、x100 への参照を失ったため、100 の参照カウントは 1 に減少します。

イミュータブルとミュータブルの違い

イミュータブルなオブジェクトとは?

Pythonの基本データ型のうち、intfloatstrtuple などはイミュータブル(不変)なオブジェクトです。これらのオブジェクトは、一度作成されるとその内容を変更できません

a = 10
b = a
a = 20

この場合、a10 を参照した後、20 に変更されています。しかし、実際には a は新しいオブジェクト 20 を参照するようになっただけで、b は依然として 10 を参照しています。
つまり、10 のオブジェクトは変更されず、a の参照先が変更されたのです。

ミュータブルなオブジェクトとは?

listdictset などは**ミュータブル(可変)**なオブジェクトです。これらのオブジェクトは、内容を変更しても同じオブジェクトのままです。

list1 = [1, 2, 3]
list2 = list1
list1.append(4)
print(list2)  # [1, 2, 3, 4]

ここでは、list14 を追加すると、list2 にも反映されます。これは、list1list2 が同じオブジェクトを参照しているためです。

コピーと参照の違い

シャローコピー(浅いコピー)

シャローコピーは、オブジェクトの参照のみをコピーします。したがって、元のオブジェクトを変更すると、コピー先にも変更が反映されます。

import copy
list1 = [1, 2, [3, 4]]
list2 = copy.copy(list1)
list1[2].append(5)
print(list2)  # [1, 2, [3, 4, 5]]

ディープコピー(深いコピー)

ディープコピーは、オブジェクトの内容をすべてコピーし、新しいオブジェクトを作成します。元のオブジェクトを変更しても、コピー先には影響を与えません。

list1 = [1, 2, [3, 4]]
list2 = copy.deepcopy(list1)
list1[2].append(5)
print(list2)  # [1, 2, [3, 4]]

まとめ:Pythonの変数は「名前」であって「箱」ではない

Pythonの変数は、**オブジェクトを参照する「名前」**に過ぎず、値を格納する「箱」ではありません。

  • イミュータブルなオブジェクトint, str, tuple など)は内容を変更できず、参照先が変更されます。
  • ミュータブルなオブジェクトlist, dict, set など)は内容を変更でき、同じオブジェクトのままです。
  • シャローコピーは参照を共有し、ディープコピーは内容を複製します。

Pythonの変数の本質を理解することで、意図しないバグを防ぎ、より効率的なコードを書くことができます。
「変数は値を格納する箱である」という誤解を避け、**「変数はオブジェクトを参照する名前である」**という本質を理解しましょう。