Pythonの変数とオブジェクトの仕組みを理解する|参照・ミュータブル・コピー・同一性

ソフトウェア

はじめに

Pythonを学んでいると、次のような疑問に出会うことがあります。

  • なぜ、別の変数からリストを変更すると元のリストも変わるのか?
  • なぜ、整数や文字列はリストのように変更できないのか?
  • なぜ、コピーしたはずのリストを変更すると元のリストにも影響するのか?
  • なぜ、同じ値を持つのに is では False になるのか?

これらは別々の問題に見えますが、共通する考え方があります。それが、「変数名はオブジェクトを参照する」という仕組みです。

Pythonでは、変数名とオブジェクトを分けて考えることで、代入や再代入、データの変更、コピー、比較などの動作を一貫して理解できます。

Pythonのデータの仕組みを理解するには、「どのオブジェクトを参照しているのか」「そのオブジェクトを変更できるのか」を考えることが重要です。

この記事では、4つのテーマを振り返りながら、Pythonの変数とオブジェクトの仕組みを体系的に整理します。

この記事でわかること

  • Pythonの変数名とオブジェクトの関係
  • ミュータブルとイミュータブルの違い
  • 代入・浅いコピー・深いコピーの仕組み
  • == と is の違い
  • 4つの知識がどのようにつながっているのか

1.Pythonの変数とオブジェクトを理解するための全体像

まず、このシリーズで学ぶ内容の関係を整理しましょう。

Pythonでは、変数名はオブジェクトを参照しています。たとえば、次のコードです。

x = [1, 2, 3]
y = x

この場合、x と y は同じリストオブジェクトを参照しています。ここから、次の4つのテーマにつながります。

  • 変数と参照:変数名はどのオブジェクトを参照しているのか?
  • ミュータブル・イミュータブル:参照先のオブジェクトは変更できるのか?
  • 代入とコピー:オブジェクトは共有されるのか、それとも別に作られるのか?
  • 等価性と同一性:値が等しいことと、同じオブジェクトであることはどう違うのか?

Pythonの変数とオブジェクトの仕組み

変数と参照が基本になり、そこから可変性・コピー・同一性の理解につながります。

2.Pythonの変数は「箱」ではない?|オブジェクトと参照を理解する

最初に理解したいのが、変数名とオブジェクトの関係です。

Pythonの変数は「値を入れる箱」と説明されることがあります。しかし、Pythonの動作を正確に理解するには、変数名はオブジェクトを参照する名前と考えることが重要です。

たとえば、次のコードです。

x = [1, 2, 3]
y = x

y.append(4)

print(x)

実行結果は次のようになります。

[1, 2, 3, 4]

y を使ってリストを変更したのに、x からも変更後の内容が見えています。これは、x と y が別々のリストを持っているのではなく、同じ1つのリストオブジェクトを参照しているからです。

変数名とオブジェクトは別のものです。複数の変数名が同じオブジェクトを参照することがあります。

次の記事では、オブジェクトの型・値・識別性や、代入・再代入・参照の共有を詳しく説明しています。

▶ Pythonの変数は「箱」ではない?|オブジェクトと参照を理解する
Pythonの変数は「箱」ではない?|オブジェクトと参照を理解する
Pythonの変数は本当に「値を入れる箱」なのでしょうか。オブジェクトと参照の仕組みを図解しながら、代入や再代入、複数の変数が同じオブジェクトを参照する仕組みを解説します。リストを変更すると別の変数からも変更が見える理由も、具体的なコードでわかりやすく説明します。

3.Pythonのミュータブル・イミュータブルとは?|型ごとの違いを整理

次に考えたいのが、参照先のオブジェクトを変更できるかどうかです。Pythonでは、オブジェクトを次の2つに分類できます。

  • ミュータブル(mutable):同じオブジェクトのまま内容を変更できる
  • イミュータブル(immutable):作成されたオブジェクトの内容を変更できない

たとえば、リストはミュータブルです。

numbers = [1, 2, 3]
numbers.append(4)

print(numbers)

実行結果は次のようになります。

[1, 2, 3, 4]

append() によって、同じリストオブジェクトの内容が変更されています。

一方、整数を表す int はイミュータブルです。x = 10 の後に x = 20 としても、整数オブジェクト 10 が変化するわけではなく、x の参照先が変わります。

代表的な型を整理すると、次のようになります。

分類 代表的な型
ミュータブル list、dict、set
イミュータブル int、float、str、tuple

ミュータブルとイミュータブルの違いは、変数名を変更できるかではなく、同じオブジェクトの内容を変更できるかです。

次の記事では、代表的な型の違いや、イミュータブルなタプルが参照するリストを変更できる理由も説明しています。

▶ Pythonのミュータブル・イミュータブルとは?|型ごとの違いを整理
Pythonのミュータブル・イミュータブルとは?|型ごとの違いを整理
Pythonのミュータブル・イミュータブルとは?list、dict、set、int、str、tupleなどの型ごとの違いを図解で解説します。再代入とオブジェクトの変更、タプル内のリストを変更できる理由、参照の共有との関係まで、初心者向けにわかりやすく説明します。

4.Pythonの代入とコピーの違いとは?|copyとdeepcopyを理解する

次に理解したいのが、代入とコピーの違いです。

Pythonでは、y = x と代入しても、新しいオブジェクトが自動的に作られるわけではありません。元のリストとは別のリストを作りたい場合には、list.copy() や copy.deepcopy() などを使います。

ただし、コピーには浅いコピーと深いコピーがあります。たとえば、次のコードです。

x = [[1, 2], [3, 4]]
y = x.copy()

y[0].append(5)

print(x)

実行結果は次のようになります。

[[1, 2, 5], [3, 4]]

コピーしたにもかかわらず、元のリストからも変更後の内容が見えています。これは、list.copy() が浅いコピーだからです。外側のリストは新しく作られますが、内側のリストへの参照は共有されます。

一方、copy.deepcopy() は、内部のオブジェクトについても必要に応じて再帰的にコピーします。今回のような入れ子のリストでは、次の違いがあります。

方法 外側のリスト 内側のリスト
y = x 共有 共有
y = x.copy() 別 共有
y = copy.deepcopy(x) 別 別

代入とコピーを使い分けるときは、どのオブジェクトを共有し、どこから別のオブジェクトにしたいのかを考えます。

次の記事では、浅いコピーと深いコピーの仕組みを図解し、外側と内側のリストを変更したときの違いも説明しています。

▶ Pythonの代入とコピーの違いとは?|copyとdeepcopyを理解する
Pythonの代入とコピーの違いとは?|copyとdeepcopyを理解する
Pythonの代入とコピーは何が違うのでしょうか。代入、浅いコピー、深いコピーの違いを、オブジェクトと参照の仕組みから図解。copyやdeepcopyの使い分けまで、具体例を使ってわかりやすく解説します。

5.Pythonの「==」と「is」の違いとは?|値とオブジェクトの同一性を理解する

最後に理解したいのが、値の等価性とオブジェクトの同一性です。Pythonには、2つのオブジェクトを比較するために == と is があります。

  • ==:値の等価性を比較する
  • is:オブジェクトの同一性を比較する

たとえば、次のコードです。

x = [1, 2, 3]
y = [1, 2, 3]

print(x == y)
print(x is y)

実行結果は次のようになります。

True
False

x と y が参照しているリストは、内容は同じですが、別のオブジェクトです。そのため、== では True、is では False になります。

なお、数値や文字列の値を比較するときは == を使います。また、None との比較には is None や is not None を使います。

== は値の等価性、is はオブジェクトの同一性を比較します。何を比較したいのかによって使い分けることが重要です。

次の記事では、id() との関係や、同じオブジェクトでも == が False になる特殊な例も説明しています。

▶ Pythonの「==」と「is」の違いとは?|値とオブジェクトの同一性を理解する
Pythonの「==」と「is」の違いとは?|値とオブジェクトの同一性を理解する
Pythonの「==」と「is」は何が違うのでしょうか。値の等価性とオブジェクトの同一性を、変数の参照や代入・コピーの仕組みから図解。id()による確認やNoneとの比較など、具体例を使って初心者向けにわかりやすく解説します。

6.4つの知識を組み合わせると何がわかる?

ここまで、変数と参照、ミュータブル・イミュータブル、代入とコピー、等価性と同一性について整理してきました。

これらの知識を組み合わせると、Pythonの動作を一貫して説明できます。たとえば、次のコードです。

x = [[1, 2], [3, 4]]
y = x.copy()

print(x == y)
print(x is y)
print(x[0] is y[0])

y[0].append(5)

print(x)
print(y)

実行結果は次のようになります。

True
False
True
[[1, 2, 5], [3, 4]]
[[1, 2, 5], [3, 4]]

なぜ、このような結果になるのでしょうか。

まず、y = x.copy() によって浅いコピーが作られます。このとき、外側のリストは別のオブジェクトになりますが、内側のリストは共有されます。

そのため、比較結果は次のようになります。

  • x == y は、リストの内容が等しいため True
  • x is y は、外側のリストが別のオブジェクトなので False
  • x[0] is y[0] は、内側のリストが共有されているため True

さらに、内側のリストはミュータブルです。そのため、y[0].append(5) によって共有されている内側のリストが変更され、x と y の両方から変更後の内容が見えます。

浅いコピーにおける参照・可変性・同一性の関係

この例では、4つの知識がすべて関係しています。

  • 変数と参照:x と y はそれぞれリストを参照する
  • ミュータブル:共有されている内側のリストを変更できる
  • 浅いコピー:外側は別、内側は共有される
  • 等価性と同一性:== と is で異なる関係を確認できる

Pythonの動作を理解するときは、「どのオブジェクトを参照しているか」「同じオブジェクトを共有しているか」「そのオブジェクトを変更できるか」を組み合わせて考えます。

まとめ

今回は、Pythonの変数とオブジェクトの仕組みについて、4つの記事を通して整理しました。

  • Pythonの変数名は、オブジェクトを参照する名前である
  • 複数の変数名が同じオブジェクトを参照することがある
  • ミュータブルなオブジェクトは、同じオブジェクトのまま内容を変更できる
  • イミュータブルなオブジェクトは、作成後に内容を変更できない
  • 代入はオブジェクトをコピーする操作ではない
  • 浅いコピーでは、外側のオブジェクトを新しく作り、内部の参照先を共有する
  • 深いコピーでは、内部のオブジェクトも必要に応じて再帰的にコピーする
  • == は値の等価性、is はオブジェクトの同一性を比較する

Pythonでは、変数名とオブジェクトを分けて考え、「どのオブジェクトを参照しているのか」「そのオブジェクトを変更できるのか」を意識することが重要です。

この考え方は、Pythonの関数やクラスを理解するうえでも重要な土台になります。

Part 1の個別記事一覧

それぞれのテーマを詳しく学びたい場合は、次の記事を順番に読むのがおすすめです。

学習順 テーマ 個別記事
① 変数と参照 Pythonの変数は「箱」ではない?
② 可変性 ミュータブル・イミュータブルとは?
③ 代入とコピー 代入とコピーの違いとは?
④ 等価性と同一性 「==」と「is」の違いとは?

次に学ぶテーマ

次のPart 2では、今回の知識を発展させて、Pythonの関数とスコープの仕組みを説明していきます。まずは、関数に引数がどのように渡されるのかを、変数と参照の関係から考えていきましょう。

▶ Pythonの関数の引数はどう渡される?|変数と参照から理解する
Pythonの関数の引数はどう渡される?|変数と参照から理解する
Pythonの関数の引数は値渡し?参照渡し?変数とオブジェクトの参照関係を図とコードで解説。整数を再代入しても呼び出し元が変わらず、リストを変更すると影響する理由を、Pythonの仕組みから理解します。

シリーズ全体を学ぶ

▶ Pythonの言語仕様をしくみから理解する

(準備中)

関連書籍

Pythonの文法だけでなく、オブジェクトやデータモデルなど、Pythonの仕組みをもう少し深く理解したい人には『入門 Python 3』もおすすめです。

Amazon.co.jp
ソフトウェア
スポンサーリンク
toshi

京都市在住のエンジニア。読書好きが高じてブログを始め、現在は数学やAI・ソフトウェアを中心に、おすすめの小説についても記事を書いています。

toshiをフォローする
シェアする
toshiをフォローする

コメント

タイトルとURLをコピーしました