Pythonで〇×ゲームのAIを一から作成する　その8　Python の変数と代入処理

# 目次と前回の記事

https://qiita.com/ysgeso/items/2381dd4e3283cbed49a0

https://qiita.com/ysgeso/items/911fafe3f80a805165d1

# 前回のおさらい

前回の記事では、欠陥のあるゲーム盤を初期化するプログラムの問題点を説明するために、プログラム A と、プログラム B を紹介しました。

プログラム B は、欠陥のあるゲーム盤を初期化するプログラムと、__同じ種類の処理__ を行う、より簡単なプログラムです。

その後で、問題点を理解するために必要な知識として __Python のデータとオブジェクト__ について説明しました。

__欠陥のあるゲーム盤を初期化するプログラム__
```python
board = [[" "] * 3] * 3
board[1][0] = "〇"
print(board)
```
実行結果
```
[['〇', ' ', ' '], ['〇', ' ', ' '], ['〇', ' ', ' ']]
```

__プログラム A__
```python
a = 5
b = a
a = 2
print(a)
print(b)
```
実行結果
```
2
5
```

__プログラム B__

```python
a = [1, 2, 3]
b = a
a[0] = 2
print(a)
print(b)
```
実行結果
```
[2, 2, 3]
[2, 3, 3]
```

# Python の変数と代入処理

プログラム A、B の 1 ~ 3 行目はいずれも __代入文__ です。そのため、プログラム A、B で行われている処理を理解するためには、Python の代入文が行う __代入処理__ について理解する必要があります。

プログラム A、B の 1 ~ 3 行目では、変数に「数値型のデータを代入する」、「list を代入する」、「別の変数を代入する」など、一見すると異なる種類の処理が行われているように __見える__ かもしれません。しかし、実際には Python の代入処理では、__常に同じ種類の処理__ が行われます。

今回の記事では、最初に Python の代入文で行われる処理を説明し、その後で プログラム A、B で記述されている代入文を具体例に、それぞれの代入文で行われる処理について解説し、どの代入文でも下記で説明する __同じ種類の処理__ が行われていることを示します。

下記が、Python の代入文で行われる代入処理の説明です。

:::note warn
Python の代入文は、変数に `=` の右に記述された __式の計算結果__ を管理する __オブジェクトの id を格納する__ 処理である。
:::

## Python の変数に関するよくある誤解

代入文は、__変数__ に対して行われる処理なので、代入で行われる処理を理解するためには、Python の __変数の仕組み__ について理解する必要があります。

プログラム言語の変数は、プログラムが扱うデータを __格納__ し、好きな時に __取り出して利用__ できるという __入れ物__ のような性質を持ちます。そのため、変数を「__代入したデータを格納__ するための __箱__」で例えて説明することが良くあります。


この例えでは、代入文は「変数を表す箱に __代入するデータを直接入れて__ 格納する」という風に例えられます。また、変数の値を使って計算する場合は、「箱の中からデータを __直接取り出して__」計算するという風に例えられます。

プログラム A の 1 行目の `a = 5` という代入文で行われる処理を、この例えで図示すると、以下のようになります。なお、下図の図形や線は以下のような意味を持ちます。

* 変数を表す箱を __直方体__ の図形[^1]で表現する
* 直方体の上の文字で __変数の名前__ を表す
* 直方体の中の文字で __変数が格納するデータ__ を表す[^2]
* データの代入を __黒い矢印__ で表現する
* 青い図形の矢印の先の図でプログラムの __代入処理の結果__ を表現する

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3524802/f9ec2dd0-ca27-63e2-88da-40006a21cc57.png" width="300">

C 言語などのプログラミング言語では、この「箱の中に、代入するデータを __直接入れて格納する__」という例えで変数を説明するのは正しいのですが、Python ではこの例えは __正確ではありません__。変数を __箱で例える__ こと自体は __間違っていない__ のですが、Python の変数は箱の中に、代入するデータを __直接格納しない__ という点が間違っています。もし Python の変数を上記のように理解していた場合は、__認識を改めて下さい__。

:::note
「箱の中に、代入するデータを直接入れて格納する」という例えは Python の変数を説明する例えとしては __間違っている__ のですが、おそらく以下のような理由で実際に Python の変数の説明でも __使われることがある__ ようです。

* この例えの説明のほうが直観的でわかりやすい
* C 言語のように、この例えによる説明が正しいプログラム言語が存在するので、それらを学んだことがある人にとってもわかりやすい
* Python の __数値型__ や __文字列型__ など、一部のデータ型の場合は、この例えの説明でも変数の処理をうまく説明できてしまう

ただし、上記の間違った例えで Python の変数に関する処理を理解してしまうと、その誤解のせいで Python のプログラムを __正しく記述できなくなる__ 可能性があるため、正確な処理を理解しておくことは重要です。
:::

## 数値型のリテラルの代入（プログラム A の 1 行目）

忘れている人がいるかもしれないので、前回の記事で説明した __リテラル__ という用語と、オブジェクトの作成についておさらいします。

:::note warn
__リテラル__ とは、`5` や `[1, 2, 3]` のように、__プログラムの中に直接記述されたデータ__ のことを表します。Python では、リテラルが記述された文を実行すると、そのリテラルを管理する __新しいオブジェクトが作成__ されます。
:::

Python の変数は、先程の図のように「変数に代入されたデータを直接格納」しているのではなく、「変数に代入されたデータを管理する __オブジェクトの id__」を格納しています。

プログラム A の 1 行目の `a = 5` を実行すると、Python では以下のような処理が行われます。

1. `5` はリテラルなので、数値型の `5` を管理する __オブジェクトが新しく作られる__
2. 作成された __オブジェクトの id__ を `a` に格納する

下図は上記の処理を図示したものです。図の点線などの意味は以下の通りです。

* オブジェクトの id を表す数字を __赤い文字__ で表記する
* 変数が格納する __オブジェクトの id__ が __参照するオブジェクト__ を __赤い点線__ で表記する

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3524802/3bc2e02d-2ad4-1933-57d4-550e0589d390.png" width="400">

上図が示すように、代入処理によって変数が格納するのは、変数に代入した `5` という数値型のデータではなく、__`5` を管理するオブジェクトの id__ です。

このように、変数に代入されたデータは、その変数が直接格納して __管理していない__ ため、変数から __直接取り出す__ ことは __できません__。

そのため、変数に代入されたデータは、以下の手順で、変数が格納するオブジェクトの id が __参照__ するオブジェクトを介して __間接的に__ 取り出して利用します。

1. 変数に格納された __id__ を取り出す
2. 取り出した id を持つオブジェクトを __探し出す__
3. 探し出したオブジェクトが __管理するデータ__ を取り出す

上図の変数の `a` に代入されたデータを取り出す手順は以下のようになります。

1. `a` に格納された `123` というオブジェクトの id を取り出す
2. `123` という id を持つオブジェクトを探し出す
3. 探し出したオブジェクトが管理する `5` というデータを取り出して利用する

上記の __オブジェクトの id に関する処理__ は、Python が __すべて自動で行ってくれる__ ので、「変数がデータを直接格納する」という間違ったイメージでプログラムを記述しても問題が発生しない場合は確かに存在します。しかし、そのようなイメージでプログラムを記述すると、「欠陥のあるゲーム盤を初期化するプログラム」のように __大きな問題が発生する場合がある__ ので、正しい認識を持つことは __非常に重要__ です。

:::note
図の赤い点線が表す __参照__ は __概念的__ なもので __実体があるわけではありません__。例えば、本の中に「詳しくは〇〇という本を参照して下さい」という、他の本への __参照を表す文章__ が書かれていたとしても、その文章から〇〇という本に向かって実体がある線で __結ばれることがない__ 点に似ています。

参照を表す識別子は、参照先のものを探すための __手掛かり__ にすぎないので __それだけでは役に立ちません__。識別子を元に、参照先のものを __探すための仕組み__ が必要となります。例えば外国のような __見知らぬ場所の住所__ が書かれた紙があっても、__それだけでは__ その住所の場所にたどり着くことは __不可能__ です。住所を元に __場所を探す__ ための地図のような __道案内をするもの__ があってはじめて住所が意味を持つようになるのです。

なお、Python にはオブジェクトの id を元に、オブジェクトを __探す仕組みが備わっている__ のでその点は問題はありません。
:::

### まとめと用語の整理

:::note warn
ここまでの内容をまとめると以下のようになります。

* Python の変数は、代入されたデータを __直接格納しない__
* Python の変数は、代入されたデータを管理する __オブジェクトの id__ を格納する
* Python の変数に代入されたデータは、変数に格納された id を介して __間接的に__ データを取り出して利用する
:::

上記のまとめで説明したように、変数は以下の __2 種類のデータ__ を扱います

* 変数が __直接__ 格納する「__オブジェクトの id__ のデータ」
* 変数が直接格納するオブジェクトの id を介して __間接的__ に扱う、「__変数に代入された__ データ 」

:::note warn
変数が扱うこの 2 種類のデータを __区別__ するために、本記事では以下のように用語を使い分けることにします。

* 変数が直接格納する __オブジェクトの id__ のデータのことを、「__変数に格納されたオブジェクトの id__」、または単に「__変数に格納された id__」と表記する
* 変数に代入されたデータのことをこれまで通り、「__変数に代入された値__」、または単に「__変数の値__」と表記する

また、変数が格納したオブジェクトの id が __参照__ するオブジェクトの事を、「__変数が参照するオブジェクト__」のように表記します。
:::

:::note
変数に代入されたデータのことを、「変数が参照する値」のように表記することがありますが、「変数が参照するオブジェクト」と __区別しづらい__ ので、本記事ではそのような表記は行わないことにします。
:::

## Python がデータを直接格納しない理由

Python の変数が、オブジェクトを介して __間接的に代入するデータを扱う__ ことが、分かりづらいと感じた人が多いかもしれません。また、変数に代入したデータを取り出して利用する際に、上記で説明したような 3 つの手順を行う必要があるため、わかりづらいだけでなく、効率が悪いというデメリットを感じた人も多いかもしれません。

しかし、変数に直接データを格納するようにすると、いくつかの __デメリット__ が発生します。デメリットの原因の 1 つは、プログラムが扱う __データの種類によって__、データの __サイズが大きく異なる__ 点にあります。例えば `"abc"` という文字列は、3 バイトのデータで表現できますが、`1` のような整数は一般的に 4 バイト のデータで表現されます。また、画像や動画などのデータは、非常に小さいものは数バイト、大きいものは数ギガバイトのようにその大きさは千差万別です。そのため、変数が直接データを格納する場合は、データの種類によって __異なる大きさの入れ物__ を用意する必要があります。具体例については割愛しますが、このことは大きなデメリットになります。

一方、データをオブジェクトで管理し、オブジェクトの id を変数に格納する場合、オブジェクトの id を表す __データのサイズ__ を同じバイト数のデータで __統一する__ ことができます。そのため、どんな種類のデータを扱う変数であっても、__同じ大きさのデータ__ を格納する __共通の入れ物__ があれば良くなります。これはプログラムが変数を扱う際に、大きなメリットになります。例えば、Python では、同じ変数に任意のデータ型のデータを代入できますが、これは変数がオブジェクトの id でデータを扱っているからです。

:::note
現実世界の例では、郵便番号は 7 桁で、電話番号は 10 桁で統一されています。そのため、郵便番号や電話番号を記述する書類は、それらの記入欄を 7 桁または 10 桁分の数字が入るような大きさの欄で統一できます。
:::

Python がデータを直接格納しない理由は他にもありますが、本記事でここまで説明した知識ではうまく説明できないので割愛します。

なお、変数にデータを直接格納することには、メリットもあります。例えばプログラムの処理速度の面では、変数に直接データを格納したほうが有利になります。そのため、プログラミング言語によって、変数がデータをどのように扱うかは異なります。

## list のデータの代入（プログラム B の 1 行目）　

プログラム B の 1 行目の `[1, 2, 3]` は __リテラル__ です。そのため、`a = [1, 2, 3]` の処理は、以下のように、プログラム A の 1 行目の処理と __同じ種類の処理__ が行われます。異なるのは下記の手順 1 で新しく作られるオブジェクトが管理するデータが、数値型の `5` であるか、list の `[1, 2, 3]` であるか __だけ__ です。

1. list の `[1, 2, 3]` を管理するオブジェクトが新しく作られる
2. 作成された __オブジェクトの id__ を `a` に格納する

下図は上記のプログラム B の 1 行目の処理を表しています。その下の、プログラム A の 1 行目の処理を表す図と比べてみて、どちらも __同じ種類の処理__ が行われていることを確認して下さい。

:::note
プログラム B の 1 行目では、実際には下図より複雑な処理が行われますが、__分かりやすさを重視__ して今回の記事では下記のような図にしました。詳細は次回の記事で説明します。
:::

プログラム B の 1 行目の処理
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3524802/44600e35-cc4c-bb6d-8ad5-08bc6a6ef2e1.png" width="350"><br>
プログラム A の 1 行目の処理
 <img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3524802/3bc2e02d-2ad4-1933-57d4-550e0589d390.png" width="330">
 
プログラム A と B の 1 行目では、数値型のデータと list を変数に代入しましたが、Python の __他のデータ型でも同じ種類の処理__ が行われます。これは重要なことなので、「Python の代入処理で行われる処理」の最初に書いたことを再掲します。

:::note warn
Python の代入処理は、変数に `=` の右に記述された __式の計算結果を管理するオブジェクトの id を格納する__ 処理である。
:::

:::note
上記のノートの説明内で、「__式の計算結果を__」と記述されている点が気になっている人がいるかもしれませんので補足します。

一般的に、式という用語から連想されるのは `1 + 2` のような、演算子を使って何らかの計算を行うものでしょう。しかし `5` のように __何の計算も行わない場合__ も __式の一種__ です。

従って、`a = 5` のような場合でも、`5` は __式__ なので、__`5` そのもの__ を式の計算結果とみなして代入処理が行われます。これは、次で説明する `a = b` のような変数を直接代入する場合でも同様です。
:::

## 別の変数の直接代入（プログラム A、B の 2 行目）

プログラム A、B の 1 行目では、`5` や `[1, 2, 3]` のようなプログラムに直接記述されたデータである __リテラル__ を変数に代入していました。

それに対して、プログラム A、B の 2 行目の `b = a` は、__別の変数を直接代入__ するという代入処理です。前回の記事で説明したように、変数が記述された文を実行する場合は、その変数に代入された値を管理するオブジェクトは作成済なので、新しいオブジェクトが __作られることはありません__。従って、`b = a` を実行すると、 `a` に代入されたデータを管理する __オブジェクトの id__ が、`b` に格納されます。

プログラム A、B の 2 行目の処理は、次の 3 ~ 5 行目の実行結果から、`a` に代入されている __データの種類__ によって、データを複製[^3]するかどうかが変わるように __見えます__。そのため、プログラム A と B で異なる処理が行われるように見える原因を、以下のように理解している人が多いのではないでしょうか？しかし、この理解は __間違っています__。

* プログラム A の 3 行目を実行した結果、`a` と `b` に異なるデータが代入されるのは、2 行目の `b = a` の処理で、`a` に代入されていた数値型の `5` が __複製されて__ `b` に代入されたことが原因である
* プログラム B の 3 行目を実行した結果、`a` と `b` に同じデータが代入されるのは、2 行目の `b = a` の処理で、`a` に代入されていた list の `[1, 2, 3]` が __複製されずに__ `b` に代入されたことによって __共有__ されたことが原因である

これまで何度も述べてきたように、Python の代入文では、常に下記の処理が行われます。このことは、「変数に __別の変数を直接代入__ する」という代入文でも変わりはありません。

:::note warn
Python の代入処理は、変数に `=` の右に記述された __式の計算結果を管理するオブジェクトの id を格納する__ 処理である。
:::

「変数に別の変数を直接代入する」という代入文で行われる処理では、代入元の変数の値が __どのようなデータ型__ のデータであっても、「代入元の変数に格納された __id__ を複製して、代入先の変数に格納する」という処理が行われます。その際に重要となるのは、__代入するデータを複製しない__ ということです。

以上の事から、Python の代入処理は __どのような場合であっても__、以下のような性質を持ちます。

:::note warn
__Python の代入処理の性質__

* オブジェクトの id という、オブジェクトへの __参照を複製__ する
* 代入する __データは複製されない__
:::

実際に、プログラム A、B の 2 行目の `b = a` では、`a` には数値型のデータの `5` と、list の `[1, 2, 3]` という異なるデータ型が代入されていますが、どちらの場合でも、データそのものは __複製されず__、オブジェクトの id という __オブジェクトへの参照が複製__ されます。この後で、実際にそのことを確認します。

### プログラム A の 2 行目の処理

下図は、プログラム A の 2 行目の処理を図示したものです。図の右で `b` と `a` が __別々に__ 同じオブジェクトの id のデータを格納していることから、`a` が格納していたオブジェクトの id が __複製されて__ `b` に格納されていることがわかります。また、図の右で `5` を管理するオブジェクトが 1 つかないことから、代入するデータそのものが __複製されていない__ ことがわかります。

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3524802/63ef12e2-e753-0bc9-f76c-618a3b0cca47.png" width="400">

`b = a` の処理を実行しても、`a` が参照するオブジェクトが変化することはありません。図からわかるように、結果として `b` と `a` が `5` というデータを管理する __同一のオブジェクト__ を参照することで、__同一のデータを共有__ するようになります。

このことをプログラムで確認してみます。`id` という関数の `()` の中に、__変数を直接記述__ した場合は、その変数が __参照するオブジェクトの id__ が得られます。そのため、`id` を使って、異なる変数が __同じオブジェクトを参照__ して __データを共有している__ かどうかを __確認__ することができます。

下記のプログラムは、プログラム A の 1、2 行目を実行した後で、`a` と `b` が参照するオブジェクトの id を表示しています。実行結果から、`a` と `b` が同じオブジェクトを参照してデータを共有していることがわかります。

```python
a = 5
b = a
print(id(a))
print(id(b))
```
実行結果
```
140731432670120
140731432670120
```

### プログラム B の 2 行目の処理

プログラム B の 2 行目でも下図のように __同じ種類の処理__ が行われます。その下の、プログラム A の 2 行目の処理を表す図と見比べて、どちらの `b = a` も、`b` が `a` と __同一のオブジェクト__ を参照し、__同一のデータを共有__ するようにする処理であることを確認して下さい。

プログラム B の 2 行目の処理
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3524802/17b07601-dbc1-e92a-255b-660153f3bd8e.png" width="300"><br>

プログラム A の 2 行目の処理
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3524802/63ef12e2-e753-0bc9-f76c-618a3b0cca47.png" width="300">

また、下記のプログラムで先程と同様に、プログラム B の場合でも、`a` と `b` が同じオブジェクトを参照してデータを共有していることを確認することができます。

```python
a = [1, 2, 3]
b = a
print(id(a))
print(id(b))
```
実行結果
```
2414732444352
2414732444352
```

上記の結果から、以下のことがわかります。

:::note warn
Python で、__変数に別の変数を直接代入__ する処理は __代入するデータの種類に関わらず__、代入元の変数の値を管理するオブジェクトの id を複製して、代入先の変数が格納することによって、__2 つの変数が、同一のデータを共有する__ 処理である。
:::

## 変数の上書き（プログラム A の 3 行目の処理）

下記のプログラム A の 3 行目の `a = 2` では、1 行目 や 2 行目と異なり、__既にデータが代入されている__ 変数 `a` に、別の値である `2` を代入して __上書き__ するという処理を行っています。

```python
a = 5
b = a
a = 2
```


このような場合に、下図のように、`a` が参照するオブジェクトが管理するデータを `2` で直接上書きするような処理が行われると __誤解している__ 人がいるかもしれませんが、__Python では下図のような処理は行われません__。

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3524802/c349f83f-e0b5-be15-3959-48ab8cf3aa12.png" width="300">

上図のような処理が行われない理由は、上図の処理が、『Python の代入文は、変数に `=` の右に記述された式の計算結果を管理する __オブジェクトの id を格納する処理__ である』という説明と __矛盾する__ からです。

また、仮に上図のような処理が行われてしまうと、__他の変数の値まで同時に変わってしまう__ という、__プログラム B と同様の問題__ が発生してしまいます。その問題について図で説明します。

プログラム A では、2 行目の `b = a` で、`b` に値が代入されます。先程の図ではこの `b` を省略していましたが、`b` を加えて プログラム A の 3 行目の処理を図にすると、以下のようになります。

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3524802/60180771-651b-1626-fa4c-005f0bdc4d31.png" width="350">

先程説明したように、プログラム A の 2 行目の `b = a` を実行すると、上図左のように `a` と `b` が、__同一__ の数値型の `5` を管理する「id が 123 のオブジェクト」を参照して、データを __共有__ するようになります。

この状態で、上図右のように、「id が 123 のオブジェクト」が管理するデータを `2` に書き換えてしまうと、`a` だけでなく、`b` の値も `5` から `2` に変化してしまいます。`a` と `b` が同じデータを __共有__ しているため、オブジェクトが管理するデータの内容を __直接書き替え__ てしまうと、`a` と `b` の値が __同時に変化__ してしまうのです。

大事なことなので何度も繰り返しますが、Python の代入で行われる処理は、代入先の変数にデータが既に代入されているか __どうかに関わらず__、下記の処理が行われます。

:::note warn
Python の代入文は、変数に `=` の右に記述された __式の計算結果__ を管理する __オブジェクトの id を格納する__ 処理である。
:::

従って、プログラム A の 3 行目の `a = 2` で行われる処理は、実際には下図のような処理が行われます。下図からわかるように、`a` が参照するオブジェクト __だけ__ が、`a` に代入されたデータを管理するオブジェクトに変更されます。従って、`a` が参照するオブジェクトが __変更されても__、`b` が参照するオブジェクトが __変わることはありません__。そのため `b = a` を実行した後で `a` に別の値を代入した場合は、`a` と `b` のデータの __共有が解除__ され、`a` と `b` に代入された値は __別々のデータ__ になります。

上記が、プログラム A の 2 行目の `b = a` によって、実際にはデータを __複製していない__ にも関わらず、データが __複製されたように見える__ 理由です。

プログラム A の 3 行目の処理
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3524802/e0dab89b-78ac-53e9-4af1-07fad3733494.png" width="300">

下図は `a` にデータが __代入されていない時__ に `a = 5` を実行する、プログラム A の 1 行目の処理です。上図と見比べて、`a` に値が代入されているかどうかに関わらず、`a` に関しては __同じ種類の処理が行われている__ ことを確認して下さい。

プログラム A の 1 行目の処理
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3524802/3bc2e02d-2ad4-1933-57d4-550e0589d390.png" width="300">

:::note warn
ここまでの内容をまとめると以下のようになります。

* __変数に別の変数を代入__ する処理により、2 つの変数が __同じデータを共有する__ ようになる
* 代入処理を行う前に、__代入先の変数__ と __同じデータを共有する変数__ があった場合は、代入処理によって __共有が解除__ され、結果として「__その変数__」と「__共有していた変数__」の値が __異なるようになる__
:::

## プログラム B の 3 行目の処理に対する錯覚

ここまでで、プログラム A が行う処理を順に説明し、プログラム A を実行した結果、`a` と `b` に __別のデータが代入__ される理由を説明しました。

一方、下記のプログラム B を実行すると実行結果から `a` と `b` に 同じデータが代入されているように見えます。

```python
a = [1, 2, 3]
b = a
a[0] = 2
print(a)
print(b)
```
実行結果
```
[2, 2, 3]
[2, 2, 3]
```

プログラム A の処理に対し、プログラム B でこのような奇妙な処理が行われているように見えるのは、おそらく以下のような __錯覚__ が原因でしょう。

* プログラム A と B は、1 行目で `a` に代入するデータの種類は違うが、2、3 行目では `a` と `2` という、__全く同じ値__ を __代入__ する式を記述しているので、__2 行目と 3 行目で行う処理は同じ種類の処理である__。
* それにも関わらず、3 行目を実行すると、プログラム A では `a` と `b` には別々の値が代入されるが、プログラム B では `a` と `b` に代入される値が同じになってしまうのは奇妙である。

上記はもっともらしく見えます。確かに、2 行目では、どちらも `b = a` という全く同じ式が記述されていますが、「__3 行目__ で行う処理は __同じ種類の処理__ である」は __本当__ でしょうか？プログラム A と B の 3 行目をよく見比べて下さい。プログラム A の 3 行目は　`a = 2` で、プログラム B の 3 行目は `a[0] = 2` なので、よく見ると __異なるプログラム__ が記述されていることがわかります。つまり、プログラム A と B の 3 行目は、__似ているように見える__ が、実際には __大きく異なる処理__ を行っていることが __錯覚の原因__ です。

プログラム A と B の 3 行目では、以下のような __異なる処理__ が行われています。

* プログラム A の 3 行目を実行すると、__`a` が__ `2` を管理するオブジェクトを参照するようになる
* プログラム B の 3 行目を実行すると、__`a` の 0 番の要素が__ `2` を管理するオブジェクトを参照するようになる

この違いを理解するためには、Python の __list__ がどのように __データを管理__ しているかについて知る必要があります。その説明については次回の記事で行います。

## プログラム B の修正

今回の記事では最後に、プログラム B の 3 行目を、プログラム A の 3 行目と __同じ種類の処理__ を行うプログラムに __修正する__ 方法について説明します。

プログラム A の 3 行目は `a = 2` のように __`a` に直接データを代入__ しています。従って、プログラム B の 3 行目で、プログラム A と同じ種類の処理を行う場合は、プログラム B の 3 行目を `a = ???` のようなプログラムに修正しなければなりません（??? には何らかのデータが入ります）。

プログラム B では 1 行目で `a` に `[1, 2, 3]` という list を代入したので、3 行目で `a` に代入するデータも __list にする__ のが自然でしょう。従って、プログラム B の 3 行目を `a = [4, 5, 6]` のように記述すれば、プログラム A と同じ種類の処理が行われることになります。

下記のプログラムは、そのようにプログラム B を修正したものです。以後は、このプログラムを、__プログラム C__ と表記します。

プログラム C
```python
a = [1, 2, 3]
b = a
a = [4, 5, 6]
print(a)
print(b)
```
<details><summary><strong>修正箇所</strong></summary>

```diff_python
a = [1, 2, 3]
b = a
- a[0] = 2
+ a = [4, 5, 6]
print(a)
print(b)

```
</details>

実行結果
```
[4, 5, 6]
[1, 2, 3]
```

修正したプログラム C を実行すると、実行結果からわかるように、プログラム A と同様に `a` と `b` に __異なるデータ__ が代入されます。

下図は、プログラム C の処理を図示したものです。その下のプログラム A の 3 行目の処理を表す図と見比べて、__同じ種類の処理__ が行われていることを確認して下さい。

プログラム C の 3 行目の処理
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3524802/6b4aac09-a818-48c8-bfc4-a4fe9aa2e6a7.png" width="350"><br>
プログラム A の 3 行目の処理
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3524802/e0dab89b-78ac-53e9-4af1-07fad3733494.png" width="300">

:::note warn
プログラム B に関する錯覚についてまとめると以下のようになります

* プログラム A、B の 2 行目が、A はデータの複製、B はデータの共有を行っているように見えるのは __錯覚__ である
* プログラム A、B の 2 行目は、__いずれも__ `b` が `a` と __同じデータを共有する__ ようにするという、__同じ種類の処理__ を行っている。
* プログラム A と プログラム B の 3 行目が同じ種類の処理を行うに見えるのは __錯覚__ である
* `a = 2` と `a[0] = 2` は似ているが、行われている処理は全く異なるものである
* プログラム B の 3 行目を `a = [4, 5, 6]` のように、__`a` に直接__ データを代入するように修正することで、プログラム A と B の 3 行目が、`a` と `b` のデータの __共有を解除__ して、異なる値を持つようにするという、__同じ種類の処理__ を行うようになる
:::

# 本記事で入力したプログラム

以下のリンクから、本記事で入力して実行した JupyterLab のファイルを見ることができます。

https://github.com/ysgeso/marubatsu/blob/master/008/marubatsu.ipynb

# 次回の記事

https://qiita.com/ysgeso/items/c6d4a2da7b8118a5a143

[^1]:適当な箱のフリーの画像が見つからなかったので、直方体の画像で代用しました
[^2]:図の左の直方体は、`5` が代入される前の `a` を表しているので、直方体の中には何も表示していません
[^3]:コピー（copy）と呼ぶことの方が多いかもしれませんが、本記事では以後は __複製__ で統一します