Pythonで〇×ゲームのAIを一から作成する　その11　Python の代入処理の補足

# 目次と前回の記事

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

https://qiita.com/ysgeso/items/59e51d983a32ac4bc5db

# 今回の記事の内容

前回までの記事では、__数値型__、__文字列型__、__list__ の 3 つのデータ型に関する代入処理しか説明しませんでした。そのため、他のデータ型の代入処理でどのような処理が行われるかについて、疑問に思っている方も多いのではないかと思います。

そこで、今回の記事では前回までの記事で説明できなかった代入処理に関する補足説明を行います。

:::note
今回の記事は、〇×ゲームの実装とは直接関係のない内容です。また、細かい話が多いせいで、かなり長くなっていますので、途中で意味が分からなくなったり、先に進みたくなった方は飛ばして次に行ってもかまわないでしょう。

ただし、今回の記事の内容は、代入処理を理解するために重要な内容ではあるので、時間ができたら戻ってきて読んでみることをお勧めします。
:::

# Python の代入処理

Python の代入処理が初心者にとってわかりづらいのは、代入するデータの種類によって、プログラム A のようにデータが __複製__ されているように見える場合と、プログラム B のようにデータが __共有__ されているように見える場合があるせいではないでしょうか。今回の記事では __何故__ そのようなことが起きるのか、そして __どのような場合に違いが生じる__ のかについて説明します。

__プログラム 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 のプログラムでデータを扱う場合、単にデータをプログラムに記述しただけでは、そのデータを後から利用することは __できません__。下記のプログラムでは、`1` というデータを記述していますが、この `1` というデータは、この __行だけで利用__ できるデータです。

```python
1
```

プログラムに記述したデータを後から __何度も利用__ したい場合は、変数にデータを代入する必要があります。変数に代入されたデータは、その内容を変更しない限り、__何度でも__ 利用できます。

:::note warn
ここでいう変数には、list の要素など、__変数と同じ性質__ を持つものを __含みます__。
:::

:::note
厳密には、他にもファイルやデータベースにデータを保存するなどの方法がありますが、それらの方法を取ったとしても、保存したデータを読み込んで利用する際には、基本的にはそのデータを __変数に代入__ する必要があります。
:::

下記のプログラムでは、1 行目で `a` に `1` を代入することで、`1` というデータを後のプログラムで __何度も利用__ しています。なお、下記のプログラムの 2、3 行目で行っている計算には特に意味はないので、実行結果は省略します。

```python
a = 1
print(a)
print(a * 5)
```

データを複製したり、共有するということは、そのデータを __後から利用__ したいということです。二度と利用しないデータであれば、それを複製したり、共有することに意味はないからです。そのため、__複製または、共有__ して利用したいデータは、__変数に代入する必要__ があります。

:::note warn
要点をまとめると以下のようになります。

* プログラムに記述したデータは、変数に代入しない限り、記述された場所で __1 度きり__ しか利用できない
* 変数に代入されたデータは、その変数に代入されたデータを変更しない限り、__何度でも利用__ できる
* __複製または、共有__ して利用したいデータは、__変数に代入する必要__ がある
:::

:::note
厳密には、二度と利用しないデータであっても、データを複製、共有することはできますが、そのような処理を行う意味はありません。そこで、以降の記事では、データを複製、共有する場合は、必ずそれらのデータを __後から利用__ できるようにするため、__変数に代入する__ という前提で話を進めます。
:::

# データの複製と共有の定義

データの複製と共有という用語をこれまで何度も使用してきましたが、その 2 つの用語の正確な意味については説明していませんでした。そこで、この 2 つの用語の意味を説明し、それぞれの __用語の定義__ を行うことにします。

:::note warn
本記事ではこれから「__データの複製__」、「__データの共有__」、「__疑似的なの複製__」などの用語を定義して使用しますが、それらは、__筆者が__ 今回の記事の説明を行うために __考えた独自の定義__ です。__他の記事や文献__ などで用いられる用語の意味とは __異なるかもしれない__ 点に注意して下さい。
:::

## 複製の定義

複製を goo 辞書で引くと「もとの物と同じ物を別に作ること」という説明がされています。

https://dictionary.goo.ne.jp/word/%E8%A4%87%E8%A3%BD/

この説明の「物」は、プログラムでは「__データ__」に対応します。これらのことから、本記事では __データの複製__ を「__あるデータを元に、同じ内容のデータを別に作ること__」と定義します。


上記の定義から、データの複製を行うと、__同じ内容__ の「__元のデータ__」と「__複製されたデータ__」の __2 つのデータ__ が存在することになります。また、先ほど説明したように、データの複製を行った後で、この 2 つのデータをプログラムで扱うことができるようにするためには、この 2 つのデータを __別々の変数に代入__ する必要があります。


:::note warn
上記をまとめると以下のようになります

* データの複製とは、「__あるデータを元に、同じ内容のデータを別に作ること__」である
* データの複製では、複製元のデータと複製先のデータを __別々の変数に代入__ して利用する
:::

## 共有の定義

共有を goo 辞書で引くと「一つの物を二人以上が共同で持つこと」という説明がされています。

https://dictionary.goo.ne.jp/word/%E5%85%B1%E6%9C%89/

この説明の「二人以上」は、プログラムでは「__2 つ以上の変数__」に対応します。また、この説明の「持つ」は、「__変数に値を代入する__」ことに対応します。これらのことから、本記事ではデータの共有を「__同一のデータを、2 つ以上の変数から共同で利用できること__」のように定義します。ここでいう「同一」とは、複製の定義の「同じ内容の」という意味とは異なり、__データを管理するオブジェクトが同一__ であるという意味を表す点に注意して下さい。

上記の定義から、データの共有を行った場合は、共有するデータを管理する __オブジェクト__ を、2 つ以上の __複数の変数__ が __参照__ することになります。

:::note warn
上記をまとめると以下のようになります

* データの共有とは、「__同一のデータを、2 つ以上の変数から共同で利用できること__」ことである
* データの共有では、共有するデータを管理する __オブジェクト__ を、2 つ以上の __複数の変数__ が __参照__ する
:::

# 代入の種類

複製と共有の定義が定まったので、次は代入処理について掘り下げていくことにします。

代入処理は、以下の 2 種類に分類することができます。

* 代入するデータを管理するオブジェクトが、他の変数から参照されて __いない__ 場合
* 代入するデータを管理するオブジェクトが、他の変数から参照されて __いる__ 場合

## 他の変数から参照されていないデータの代入

「代入するデータを管理するオブジェクトが、他の変数から __参照されていない__」場合の代入処理では、基本的には データの __複製__ も __共有__ も __行われません__。

その理由を箇条書きで説明すると以下のようになります。

* 代入するデータを管理するオブジェクトは、どの変数からも参照されていない
* 代入処理によって、そのデータを管理するオブジェクトが、変数から参照されるようになる
* 上記の事から、代入処理によって、代入されたデータを管理するオブジェクトは __1 つの変数のみ__ から参照されることになる
* このことは __複製の性質__ である「複製元のデータと、複製先のデータを __別々の変数に代入__ して利用する」に反する
* このことは __共有の性質__ である、「共有するデータを管理するオブジェクトを、2 つ以上の __複数の変数__ が参照する」に反する

従って、「__代入するデータを管理するオブジェクトが、他の変数から参照されていない__」場合の代入処理では、基本的にはデータの複製や共有のことを  __考慮する必要はありません__。

なお、「基本的」と書いたのは、__部分的に共有__ が行われる場合があるためです。そのことについては後述します。

次に、「代入するデータを管理するオブジェクトが、他の変数から参照されていない」代入処理が、__どのような場合__ に行われるかについて説明します。

### リテラルのみが記述された式を直接代入する場合

「その 7」の記事で説明したように、リテラル[^1]が記述された文が実行されると、そのリテラルを管理する __新しいオブジェクト__ が作成されます。従って、下記のプログラムのような、リテラルを直接変数に代入する処理では、新しく作成されたオブジェクトの id を変数に格納するという処理が行われます。新しく作成されたオブジェクトは、作成された時点では __どの変数からも参照されていない__ ので、この場合は __データの複製も共有も行われません__。

なお、下記の処理は前回までの記事で紹介済なので、処理の説明や図示は省略します。

```python 
a = 1
a = " "
a = [1, 2, 3]
```
### 新しいデータが生じる式を代入する場合

これまでの記事では説明していませんでしたが、Python がデータを管理するオブジェクトを __新しく作成する__ のは、リテラルが記述された文が実行された場合だけではありません。式の計算で __新しいデータが生じる__ 場合でも、そのデータを管理するオブジェクトが __新しく作られます__。

いくつか具体例を挙げて説明します。

```python
a = 1 + 2
```

上記のプログラムでは、以下のような手順で処理が行われます。なお、次の「式の中に変数が記述されている場合」で類似する代入処理を図示しますので、上記のプログラムで行われる処理の図は省略します。

1. `1` と `2` はリテラルなので、それらを管理する新しいオブジェクトを 2 つ作成する
2. 作成したオブジェクトからデータを取り出す
3. 取り出した `1` と `2` を使って、`1 + 2 = 3` を計算する
4. `3` は、式を計算した結果によって作られた __新しいデータ__ なので、`3` を管理する新しいオブジェクトを作成する
5. 作成した `3` を管理するオブジェクトの id を `a` に格納する

式の計算を行う前に、毎回新しいデータを管理するオブジェクトを作成し、その後でそのオブジェクトからデータを取り出すという処理が回りくどいと思う人がいるかもしれませんが、Python が新しいデータを扱う際は、__必ず__ そのデータを管理するオブジェクトを __作成する__ ことになっているので、このような処理が行われます。

:::note
上記のような手順で計算を行う一つの理由は、全てのデータをオブジェクトで管理することによって、式の中にどのようなデータが記述されていた場合でも、__共通する手順__ で計算を行うことができるというメリットがあるからです。
:::

先ほどの例と同様に、上記の手順 4 で __新しく作成されたオブジェクト__ は、作成された時点では __どの変数からも参照されていない__ ので、この場合も __データの複製も共有も行われません__。

### 何かを計算する式の中に変数が記述されている場合

下記のプログラムの 2 行目のように、何かを計算する式の中に変数が記述されている場合は、変数が参照するオブジェクトは __既に作成済__ なので、式の中に記述された __変数に対して__ 新しいオブジェクトが作成されることは __ありません__。

```python
a = 5
b = a + 1
```

上記のプログラムの 2 行目では、以下のような手順で処理が行われます。

1. `1` を管理するオブジェクトが作成される
2. `a` が参照するオブジェクトと、上記で作成したオブジェクトからデータを取り出す
3. 取り出した `5` と `1` を使って、`5 + 1 = 6` を計算する
4. `6` は、式を計算した結果によって作られた 新しいデータ なので、`6` を管理する新しいオブジェクトを作成する
5. 作成した `6` を管理するオブジェクトの id を `a` に格納する
 
下図は上記の処理を図示したものです。

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3524802/77207826-52a9-804f-de30-0ffdb65ac367.png" width="400">

先ほどの例と同様に、上記の手順 4 で __新しく作成されたオブジェクト__ は、作成された時点では __どの変数からも参照されていない__ ので、この場合も __データの複製も共有も行われません__。

### 複合データ型と単一データ型の定義の再掲

この記事では、複合データ型と単一データ型という用語を頻繁に使用します。この 2 つの用語は過去の記事で説明しましたが、__どちらの用語の意味__ も __本記事が独自に定義__ したものなので、その定義と性質を再掲します。

:::note warn
__複合データ型の定義と性質__
* 複合データ型とは、Python の __任意のデータ型__ を、__複数組み合わせて__ データを表現するデータ型のことである
* 複合データ型のデータは、__複数のオブジェクト__ が管理するデータから __構成__ される
* 複合データ型を __構成するオブジェクト__ は、list のインデックスのような __識別子__ を用いて __参照__ する
* 複合データ型には、list、tuple、dict などがある

__単一データ型の定義と性質__
* 単一データ型とは、データを __1 つのオブジェクトが管理__ するデータ型のことである
* 単一データ型には、数値型、文字列型、論理型などがある
:::  

### 複合データ型のリテラルの中に、変数のみが記述されているものがある場合

list のような、複合データ型のリテラルでは、データの中に __変数のみを記述__ することができます。言葉の説明では意味が分からないと思いますので、具体例を示します。

`[1, 2, 3]` という list のリテラルは、数値型の `1`、`2`、`3` という 3 つの数値型のリテラルが組み合わされて表現されています。この場合は、変数のみのデータは記述されていません。

一方、下記のプログラムの 2 行目で `b` に代入する list のリテラルは、数値型の `1` というリテラルと、変数 `a` が組み合わされて表現されています。

```python
a = 1
b = [1, a]
```

上記のように、複合データ型のリテラルの中に、変数のみが記述されているものがある場合は、`b` に代入されたデータの __一部の要素__ が、`a` と同じオブジェクトを __共有__ するという、__部分的な共有__ が行われます。なお、部分的な共有は少々複雑なので後で説明します。しばらくの間は、__部分的な共有が行われていない__ 場合の説明を行います。

なお、下記のプログラムの 2 行目の `a + 1` のように、複合データ型のリテラルの中の、__何かを計算する式__ に __変数が記述__ されている場合は、部分的な共有は __行われません__。下記のプログラムの `a + 1` に対応する list の要素には、その式の計算結果である `2` を管理する新しく作成されたオブジェクトの id が代入されます。

```python
a = 1
b = [1, a + 1]
```

### 関数呼び出しを代入する場合

関数については詳しくは今後の記事で説明する予定ですが、関数呼び出しは何らかの処理を行い、その計算結果を返すという処理を行います。関数呼び出しの計算結果は Python のデータなので、__計算結果を管理するオブジェクト__ が存在します。従って、`a = id(1)` のように、変数に関数呼び出しの計算結果を代入する場合は、計算結果を管理するオブジェクトが、__他の変数から参照されているかどうか__ によって行われる処理が異なります。

関数の計算結果を管理するオブジェクトが、他の変数から参照されているかどうかは、__関数が行う処理によって異なる__ ので、関数のプログラムを見るか、関数の使い方の説明書を見て判断するしかありません。

関数呼び出しを変数に代入する処理は、関数呼び出しの計算結果を表すデータを変数に代入する処理なので、行われる処理は __他の代入処理と変わりはありません__。そのため、今回の記事では以降は、関数呼び出しを扱わないことにします。

## 他の変数から参照されているデータの代入

「代入するデータを管理するオブジェクトが、他の変数から __参照されている__」ような代入処理は、`a = b` のような、__変数に他の変数を直接代入__ する処理で行われます。

前回までの記事で説明してきたように、このような代入処理を行うと、`b` が格納するオブジェクトの id が `a` に代入されます。その結果、`a` と `b` が __同一のオブジェクトを参照__ するようになるため、「共有するデータを管理するオブジェクトを、__2 つ以上の__ 複数の __変数が参照する__」という、__データの共有の性質を必ず満たす__ ことになります。

このことから、以下のことがわかります。

:::note warn
__変数に他の変数を直接代入__ する処理では、__常にデータを共有__ する処理が行われる。
:::

それにもかかわらず、前回までの記事で紹介した下記のプログラム A と プログラム B では、2 行目で __同じ__ `b = a` という、__変数に他の変数を直接代入する__ 処理を実行していますが、3 行目の処理の結果を見ると、プログラム A では「`a` に代入された値を `b` に __複製__ しているように __見える__」、プログラム B では「`a` に代入された値を `b` と __共有__ しているように見える」という違いが生じています。

このような処理が行われる理由については、前回までの記事で詳しく説明しましたが、この違いについて、いまだに頭が混乱している人が多いのではないでしょうか？次は、この違いが生じる理由と、どのような場合に複製しているように見えるかについて説明します。

__プログラム 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]
```

:::note warn
後で説明する「複合データ型のリテラルの中に、変数のみが記述されている」場合を除けば、代入処理によって、__データの共有__ が行われるのは、「__変数に別の変数を直接代入する__」場合 __だけ__ です。そこで、今回の記事では __以後しばらくの間__ は、特に説明がない場合は、__変数に別の変数を直接代入する処理__ のみを扱うことにします。
:::

# Python の代入に関する不正確な説明

Python の代入処理に対して、以下のような説明をたまに見かけますが、この説明は実際には __不正確__ です。なお、下記のノートは、__内容に不正確な部分__ があるので、__赤色の背景__ で表示しています。

:::note alert
変数に別の変数を直接代入した際に、下記のような処理が行われる。

* __イミュータブル__ なデータを代入すると、データが __複製__ される
* __ミュータブル__ なデータを代入すると、データが __共有__ される
:::

このような説明は、__不正確__ であるにもかかわらず、__実際に使われることがある__ ので、何故このような説明が行われるかについて説明します。

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

上記の説明で出てきた、__ミュータブル__ と __イミュータブル__ という用語について説明します。この 2 つの用語は、Python のデータ型の __性質__ を表す用語です。

Python では、データ型の種類によって、オブジェクトが管理するデータを「__後から書き換えることができない__」ものと、「__後から書き換えることができる__ 」ものの 2 つに分類できます。後から書き換えることができないデータ型のことを __イミュータブル__[^2] （変更不能）なデータ型、書き換えることができるデータ型のことを __ミュータブル__[^3] （変更可能）なデータ型と呼びます。

本記事で扱う予定のデータ型について分類すると以下のようになります。なお、() の中は Python でのデータ型の正式名称です。他にもデータ型はありますが、本記事では扱わないので割愛します。これまでに説明していないデータ型も入っていますが、それらについては必要になった時点で説明します。

* __イミュータブルなデータ型__
    * 数値型 (int、float、complex)
    * 文字列型 (str)
    * 論理型 (bool)
    * タプル型（tuple）
* __ミュータブルなデータ型__
    * リスト型（list）
    * 辞書型（dict）
    * 集合型（set）

:::note
これまで区別する必要がなかったので説明していませんでしたが、Python の数値型には「整数」を表す「int[^4]」、「浮動小数点数（小数点以下を含む数値）」を表す「float[^5]」、「複素数」を表す「complex[^6]」の 3 種類があります。これらについては、区別する必要が出てきた時点で説明します。
:::

用紙で例えると、イミュータブルなデータを書き込んだ用紙は、油性ペンで書きこんだ場合のように __後から書き換えることができません__。一方、ミュータブルなデータを書き込んだ用紙は、鉛筆で書きこんた場合のように __後から書き換えることができます__。

:::note
後から書き換えることができないものの事を英語で read only と呼びます。例えば、書き込み不能な CD ROM の ROM は read only memory の略です。
:::

## 不正確な説明が行われる理由

先程の不正確な説明を再掲します。

:::note alert
変数に別の変数を直接代入した際に、下記のような処理が行われる。

* __イミュータブル__ なデータを代入すると、データが __複製__ される
* __ミュータブル__ なデータを代入すると、データが __共有__ される
:::

先ほど説明したように、変数に別の変数を直接代入する処理は、__常に__ 複数の変数で __同じデータを共有する処理__ です。そのため、上記の __ミュータブル__ に関する説明は __正しい説明__ です。そこで、以降は不正確な上記の __イミュータブル__ に関する説明に __絞って__ 解説を行います。

「イミュータブルなデータを代入すると、データが複製される」という説明が行われるのは、おそらく以下のような理由ではないかと思います。

* 「データそのものが複製される」という処理のほうが、「データを管理するオブジェクトの id が複製される」という実際に行われている処理よりも __直観的で理解しやすい__
* 数値型や文字列型などの、良く使われるイミュータブルなデータを代入すると、実際にデータそのものが複製された場合と __同様の処理__ を行うことができるため、__実質的に__ 複製されると考えても間違っていない __場合がある__
* C 言語などのプログラム言語では、数値型のデータを代入すると確かにデータそのものが複製される

例えば、プログラム A では、データが複製されたように見える処理が行われます。

しかし、実際には、「イミュータブルなデータを代入すると、データが __複製__ される」という説明が __正しくない場合__ があります。そのため、この説明をそのまま鵜呑みにすると __間違ったプログラム__ を記述してしまう可能性があります。

## データの複製の再定義

代入処理で実際にはデータの複製が __行われていない__ にも関わらず、データの複製が行われるように __見える__ 理由について理解するためには、「データの複製」によって複製されたデータが __どのような性質を持つか__ について理解する必要があります。

本記事では「データの複製」を以下のように定義しましたが、この定義の「同じ内容のデータを別に作る」の部分が抽象的なので、より具体的な定義に修正することにします。

:::note warn
あるデータを元に、同じ内容のデータを別に作ること
:::

「別に作る」から、複製元のデータを元に、複製先のデータが __新しく作られる__ ことがわかります。以後はこの性質の事を、「__データの新規作成__」と表記することにします。また、複製元のデータを「__データ A__」、複製先のデータを「__データ B__」と表記することにします。

「同じ内容」から、「データ A」と「データ B」は同じ内容を持つことがわかります。このような性質の事を「__同値性__」と呼びます。

「別に作る」ということは、「データ Ａ」と「データ B」は、異なる __独立した__ データであることを意味します。そのため、「データ A」と「データ B」のいずれかを編集しても、もう片方のデータの内容は変化しないことがわかります。以後は、この性質の事を「__データの独立性__」と表記します。

上記のことから、「データの複製」を以下のように __定義し直す__ ことにします。

:::note warn
データの複製とは、以下のような性質を持つ処理のことである。

1. 複製元の「データ A」を元に、複製先の「データ B」を新しく作成する（__データの新規作成__）
2. データ B は、データ A と __同じ内容__ を持つ（__同値性__）
2. データ A、B のいずれかを編集しても、もう片方のデータの内容は変化しない（__データの独立性__）
:::

これまでに何度も説明してきたように、Python の代入処理では、代入するデータを管理するオブジェクトが __複製されることはありません__ ので、上記の「データの新規作成」の性質を __満たすことはありません__。このことから、「イミュータブルなデータを代入すると、データが複製される」という説明の中で使われている「複製」という __用語の意味__ が、上記の「データの複製」の定義とは __異なる意味を持つ__ ということがわかります。

:::note warn
「イミュータブルなデータを代入すると、データが複製される」という説明の中の「複製」という用語は、「複製」という用語の __一般的な意味__ とは __異なる意味__ を持つ。
:::

## 複製の目的

ところで、一般論として、物の複製を行う __目的__ は何でしょうか？考えられる目的の一つに、物を複製することで、それぞれの物に対して __個別の編集__ を行うことができるようにするというものがあるでしょう。具体例としては __ノート__ が挙げられます。店で売られているノートは、設計図を元に、同じ物が大量に __複製__ されたものです。__複製された直後__ のノートは、__どのノートも__ 全てのページが白紙で __その内容は同じ__ ですが、個別のノートには、そのノートを購入した人が __それぞれ別の内容__ を書きこんで __編集する__ ことができます。

他の目的としては、__編集を目的としない__ 複製が考えられます。具体的には、__本__ が挙げられます。本はノートと同様に、同じ物が大量に __複製__ されたものですが、一般的には本はノートと違ってその中身を __読むだけ__ で、本の中身を __編集__ して書き換えることは __しません__。

本記事で定義した「データの複製」によって作られたデータは、上記の __どちらの目的__ でも使用することが __できます__。例えば、__編集を目的としない__ 複製の例として挙げた本は、実際に鉛筆などを使って文字を書き込んで編集することができます。

## 疑似的な複製

イミュタブルなデータを管理するオブジェクトは、後からデータを編集することができなません。そこで、その性質に合わせて、「データの複製」の __条件を厳しく__ した、__複製後に編集を行うことができない__ という性質を持つデータの複製を考えることにします。そのような性質を持つデータの複製は、__編集を目的としない__ 複製の目的で __のみ__ 使用することができます。

:::note warn
複製後に編集を行うことができないデータの複製のことを、__今回の記事では__ 以後は「__疑似的な複製__」と表記し、これまでの「データの複製」のことを区別して「__完全な複製__」と表記することにします。

なお、今回の記事では「疑似的な複製」と「完全な複製」を __区別することが重要__ なので「完全な複製」という表記を行いますが、この表記は冗長なので、次回以降の記事では「完全な複製」ではなく単に「複製」と記述することにします。
:::

次に、完全な複製と __比較__ しながら、疑似的な複製に求められる __性質を考察__ し、疑似的な複製の __定義__ を定めることにします。その後で、__イミュータブルな数値型__ のデータの __代入処理__ が、__疑似的な複製の定義を満たす__ ことを示します。

### 編集不可能性

「疑似的な複製」が「完全な複製」と異なるのは、複製元と複製先の __いずれのデータも編集できない__ ということです。以後は、この性質の事を「__編集不可能性__」と表記します。

### 「データの新規作成」の削除

疑似的な複製では、完全な複製の「__データの新規作成__」の性質を __満たす必要はありません__。例えば、本に決して __書き込みを行わない__ のであれば、同じ本を __複製__ して一人一人が本を所有して読むことと、1 冊の本をみんなで __共有__ して読むことに __実質的な違いはない__ からです。そのため、疑似的な複製の定義から「データの新規作成」の性質を __削除__ することができます。

:::note
現実の世界で、1 冊の本をみんなで共有した場合は、本を複製した場合と比べて「同時に複数の人がその本を読むことが困難」、「その本を読むためにはその本がある場所まで行かなければならない」という違いがあります。

一方、プログラムでは、同じデータを複数の変数が共有した場合でも、プログラムに変数名を記述するだけで、共有した値を利用することができるので、そのような違いは __問題にはなりません__。

ピンとこない人は、皆さんが今見ている本記事のような、__ウェブページ__ を思い浮かべると良いでしょう。ウェブページは、ウェブブラウザのアドレスバーにウェブページの __識別子__ である URL を入力して閲覧しますが、その際に、__離れた場所__ にいる __複数の人__ が __同時__ に __同じ__ ウェブページを __共有__ して見ることができます。また、ウェブページを見る人が、ウェブページの内容を __編集することができない__ という「編集不可能性」の性質を持つという点でも、疑似的な複製と似ています。
:::

### 「データの独立性」の修正

疑似的な複製は __データを編集できない__ ので、「データ A、B のいずれかを編集しても、もう片方のデータの内容は変化しない」という、「データの独立性」の性質は __意味を持ちません__。しかし、「データの編集」の __意味を考察__ することで、「データの独立性」を __別の表現に修正する__ ことができます。

「データの編集」は、以下の __2 つに分類__ することができます。

* データの内容の __一部__ を編集する
* データの内容の __全て__ を編集する

上記のうちの、「内容の全てを編集する」という処理は、「全ての内容を削除し、新しい内容で上書きする」という処理を意味しますが、この処理は「データそのものを __破棄__ して、__新しいデータで置き換える__」という処理で __代替する__ ことができます。そして、そのような処理は、「__編集不可能性__」に __矛盾しません__。

分かりにくいと思いますので、このことをノートで例えます。「ノートの内容を全て消しゴムで消してから、別の内容を書きこむ」ことと、「ノートを捨てて、新しいノートに別の内容を書きこむ」ことは、行っている  __処理の内容__ は確かに __異なります__ が、そのノートの __内容を利用する立場__ から見ると、__実質的な違いはありません__。

そこで、「データの独立性」を「データ A、B のいずれかを __別のデータに入れ替え__ ても、__もう片方の__ データの __内容は変化しない__」のように修正することにします。

### 疑似的な複製の定義

上記の事から、疑似的な複製を以下のように定義する事にします。

:::note warn
疑似的な複製とは、複製元を「データ A」、複製先を「データ B」とした場合、以下のような性質を持つ処理のことである。ただし、「データ A」と「データ B」は同一のデータであっても構わないものとする。

1. データ B は、データ A と __同じ内容__ を持つ（__同値性__）
2. データ A と データ B はいずれも内容を __編集することはできない__ （__編集不可能性__）
3. データ A、B のいずれかを __別のデータに入れ替え__ ても、__もう片方__ のデータの __内容は変化しない__（__データの独立性__）
:::

理由についてはこの後で説明しますが、__イミュータブルな数値型__ のデータが代入された変数を、別の変数に代入するという処理は、__疑似的な複製の性質を満たします__。これが、「イミュータブルなデータを代入すると、データが複製される」という説明が行われる原因ではないかと思います。

この説明を、より正確な説明に直すと「イミュータブルなデータを代入すると、__複製元と複製先のデータの編集を行うことができない__、__疑似的な複製__ が行われる」のようになるでしょう。

ただし、そのように修正した説明にはまだ __不正確な部分__ がります。なぜなら、イミュータブルなデータの代入処理を行った際に、疑似的な複製の性質が __満たされない__ 場合があるからです。

そのことについては後で説明することにして、最初にイミュータブルな __数値型__ のデータの代入処理が、__疑似的な複製の定義を満たす__ ことを説明します。

### イミュータブルな数値型のデータの代入処理

プログラム A の `a = 5`、`b = a` という処理で、__イミュータブルな数値型__ のデータが代入された変数を別の変数に代入した場合、下図の右のようにイミュータブルなデータを管理するオブジェクトを、__複数の変数で共有して__ 参照することになります。図からわかるように、`5` を管理するオブジェクトは複製ではなく、共有されています。疑似的な複製では、「データの新規作成」の条件は __必要とされない__ ので、この代入処理で、新しいオブジェクトが複製されていなくても問題はありません。

なお、以降の図では __イミュータブル__ なデータを管理するオブジェクトを、ミュータブルなデータを管理するオブジェクトと __区別__ ができるように、下図のようにデータを表す長方形の背景色を __灰色__ で塗りつぶして表記することにします。

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3524802/035a7caf-36a3-5716-73e9-d225709a983d.png" width="350">

下図の左はプログラム A の 2 行目の `b = a` の処理を実行した直後の状況を表しています。図からわかるように、`a` と `b` は同一のオブジェクトを参照しているので、「データ B は、データ A と 同じ内容を持つ」という「__同値性__」の性質を満たします。

また、数値型の `5` は __イミュータブルなデータ__ なので、「データ A と データ B はいずれも内容を編集することはできない」という、「__編集不可能性__」の条件を満たします。

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3524802/6f951cf4-2573-5d89-6e88-21045fbb0b92.png" width="400">

`5` はイミュータブルなデータなので、`5` を管理するオブジェクトのデータを変更するという方法で、`a` や `b` に代入されたデータを変更することはできませんが、`a` や `b` に __別のデータを代入__ してデータを __丸ごと入れ替える__ という方法で __変数の値を変更__ することは可能です。また、そのような代入処理を行っても、変数の値が変更されるのは、__代入処理を行った変数だけ__ で、__もう片方の変数の値は変更されません__。このことから、「データ A、B のいずれかを別のデータに入れ替えても、もう片方のデータの内容は変化しない」という、「__データの独立性__」の性質を満たします。

疑似的な複製の __3 つの条件を満たす__ ことから、__イミュータブルな数値型__ のデータが代入された変数を別の変数に代入した場合に、__疑似的な複製__ が行われることが確認できました。

### 単一データ型でイミュータブルなデータの代入

上記では、数値型のデータの場合の説明を行いましたが、「__単一データ型でイミュータブル__」なデータであれば、代入処理によって __疑似的な複製__ がおこなわれます。その理由は先ほど説明した数値型のデータと同じで、そのようなデータは「__疑似的な複製__」の定義の 3 つの __条件を全て満たす__ からです。先ほどの数値型の場合と __ほぼ同じ内容__ になるので、具体的な処理の説明と図示は省略します。

そのようなデータ型には、__数値型__、__文字列型__、__論理型__ などがあります。

### 文字列型のデータに関する補足

文字列型のデータは、ミュータブルなデータだと __勘違い__ されることがあるので補足します。

C 言語など、文字列型のデータがミュータブルなデータ型であるプログラム言語が実際にありますが、Python では文字列型のデータは、__イミュータブル__ なデータ型です。

Python では `+` 演算子や、`+=` 演算子を使って、文字列を連結することができますが、何れの場合でも、連結された文字列を管理する __新しいオブジェクトが作成__ されます。連結する文字列を管理するオブジェクトのデータが __変更されることは無い__ 点に注意して下さい。

下記のプログラムでは、`a` と `b` に文字列を代入した後で、`+` 演算子で `a` と `b` を連結した文字列を `c` に代入していますが、実行結果からわかるように `a` と `b` の値は変化しません。

```python
a = "abc"
b = "def"
c = a + b
print(a)
print(b)
print(c)
```
実行結果
```
abc
def
abcdef
```

下記のプログラムの 3 行目の `a += "def"` は、`a` が参照するオブジェクトのデータに `"def"` を連結する処理のように __誤解されがち__ ですが、この処理は `a = a + "def"` と同じ処理を行います。先ほど説明したように、`+` 演算子による文字列の連結は、連結された文字列を管理する __新しいオブジェクトが作成__ されます。

そのため、下記の実行結果からわかるように、連結する前の 3 行目の `a` と連結後の 7 行目の `a` は __異なるオブジェクトを参照__ します。

また、2 行目で `b = a` を実行することで、`a` と `b` は同じデータを管理するオブジェクトを共有しますが、`a += "def"` を実行すると `a` だけが新しく作られたオブジェクトを参照するようになるので、5、6 行目で `a` と `b` を表示すると異なる内容が表示されます。

```python
1  a = "abc"
2  b = a
3  print(id(a))
4  a += "def"
5  print(a)
6  print(b)
7  print(id(a))
```

<details><summary><strong>行番号のないプログラム</strong></summary>

```diff_python
a = "abc"
b = a
print(id(a))
a += "def"
print(a)
print(b)
print(id(a))
```
</details>

実行結果
```
140731431470144
abcdef
abc
2251167654512
```

:::note
過去の記事のノートで、文字列型のデータは、`[]` を使って list のように文字列の中の特定の文字を取り出すことができることを説明しました。そのため、文字列型のデータを複合データ型だと思った人がいるかもしれませんが、実際には __そうではありません__。

複合データ型は、__任意の Python のデータ型__ のデータを __組み合わせて__ データを表現できるという性質を持ちますが、文字列型のデータは、__文字以外のデータを扱うことができません__。その理由は、文字列型のデータは、文字列の中の個々の文字を __異なる__ オブジェクトで __管理していない__ からです。
:::

## 複合データ型の代入処理

複合データ型のデータは、イミュータブルなデータ __であっても__、代入処理によって __疑似的な複製__ が行われるとは __限りません__。以下の説明では、イミュータブルな複合データ型である tuple の説明を行い、tuple の代入処理によって疑似的な複製が __行われない具体例__ を挙げます。その後で、Python の代入処理で、__疑似的な複製が行われる条件__ について説明します。

### tuple の性質

Python には __tuple__[^7]（タプル）という、__複合データ型__ で、なおかつ __イミュータブル__ なデータ型があります。tuple は後からその要素の値を変更できないという、__イミュータブル__ な性質を除けば、__list とほぼ同様の性質__ を持ちます。

tuple は複数のデータをプログラムで扱う際に、そのデータが __後から変更されることがない__ ことが __あらかじめわかっている__ ような場合などで利用します。また、tuple は dict というデータ型のキーとして利用することができます。dict や tuple の具体的な使用方法については、今後の記事で説明する予定です。

### tuple の記述方法

tuple のリテラルは `()` の中に、list と同様に、その要素を半角の `,` で区切って記述します。下記のプログラムは tuple の記述例と、その記述例と同じ要素を持つ list の記述例です。下記のプログラムでは、`t` という変数に、様々な tuple を代入し、そのすぐ下の行で、同じ要素を持つ list を `l` （半角の小文字のエルです）という変数に代入しています。

`t` と `l` に代入するデータの違いは、`()` が `[]` に変更されているだけなので、特に難しい点はないと思います。なお、1 行目のように、1 つも要素を持たない tuple のことを空の tuple と呼びます。

```python
t = ()
l = []
t = (1, 2, 3)
l = [1, 2, 3]
t = ("〇", "×")
l = ["〇", "×"]
t = ((" ", " ", " "), (" ", " ", " "), (" ", " ", " "))
l = [[" ", " ", " "], [" ", " ", " "], [" ", " ", " "]]
```

:::note
tuple と list のリテラルの記述方法の違いは、基本的には `()` と `[]` だけですが、__要素が 1 つしかない__ tuple の場合 __のみ__、list の場合と比較して若干記述方法が異なります。

要素が 1 つしかない tuple は、下記のプログラムの 1 行目のように、__要素の後ろに必ず半角の `,` を記述__ する必要があります。

```python
t = (1, )
print(t)
t = (1)
print(t)
```
実行結果
```
(1,)
1
```

その理由は、tuple を記述する際に使用する __`()` という記号__ が、__数式でも使われる__ からです。例えば、`(1 + 2) * 3` という式で使われる `()` は、`()` の中に記述された式を優先して計算するという意味を持ちます。そのため、`(1)` のように記述すると、その `()` は数式で使われる `()` と同じ意味だとみなされるため、`(1)` は数値の `1` だとみなされてしまいます。上記のプログラムの 4 行目を実行すると、実行結果の 2 行目で `1` が表示されるのはそのためです。

要素が 1 つしかない tuple の場合に、要素の後ろに `,` を記述するのは、上記の数式の場合と __区別ができる__ ようにするためです。
:::

### tuple の要素の参照

tuple は list と同様に、`[]` の中に __インデックスを記述__ して __要素を参照__ します。下のプログラムは tuple の中の特定の要素を表示しています。また、4 行目のように、list と同様の方法で、2 次元配列を表す tuple の要素を表示することもできます。

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

### tuple の要素への代入

tuple は __イミュータブル__ なデータ型なので、その要素に値を代入すると下記のプログラムのように、__エラーが発生__ します。また、具体例は省略しますが、`append` を使って後から tuple に要素を追加することもできません。

```python
t = (1, 2, 3)
t[0] = 2
```
実行結果
```
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
Cell In[8], line 2
      1 t = (1, 2, 3)
----> 2 t[0] = 2

TypeError: 'tuple' object does not support item assignment
```

:::note
上記のエラーメッセージは、以下のような意味を持ちます。

* __TypeError__
データ型（Type）に関するエラー
* __'tuple' object does not support item assignment__
tuple なオブジェクト（object）は、要素（item）に値を割り当てる（代入のこと）（assignment）する処理を提供（support）しない（does not）
:::

### tuple に関するよくある勘違い

tuple は __イミュータブル__ なデータなので、下記のように、tuple が代入された変数を、別の tuple で上書きできないと __勘違い__ されがちです。しかし下記のプログラムは問題なく実行することができます。

```python
t = (1, 2, 3)
t = (4, 5, 6)
print(t)
```
実行結果
```
(4, 5, 6)
```

イミュータブルなデータである tuple が __変更できないのは__、その tuple を管理する __オブジェクトのデータ__ です。具体的には、tuple の __要素の値を変更__ することが __できません__。上記のプログラムの 2 行目で行っているのは、tuple の要素を変更しているのではなく、`t` に全く __別の tuple を代入__ して丸ごと __入れ替える__ という処理なので、問題なく実行できます。

下記のプログラムは、`+` 演算子を使って tuple を結合していますが、要素の値を変更できないはずの tuple で結合ができてしまう点がおかしいと思う人がいるかもしれません。このように考える人は、tuple に対する `+` 演算子の処理を __誤解__ しています。

```python
a = (1, 2, 3)
b = (4, 5, 6)
c = a + b
print(a)
print(b)
print(c)
```
実行結果
```
(1, 2, 3)
(4, 5, 6)
(1, 2, 3, 4, 5, 6)
```

2 つの tuple に対して `+` 演算子で計算を行うと、2 つの tuple を結合したデータを管理する __新しいオブジェクト__ が作られます。元の 2 つの tuple は全く変化しないため、エラーにはなりません。実際に上記のプログラムの実行結果から、結合に使われた `a` と `b` の内容が全く変化していないことを確認して下さい。

:::note
下記の内容は、細かい話なのでノートにしました。

list では `+=` 演算子は、list に要素を追加するという処理が行われますが、tuple の場合は、イミュータブルなので、要素の追加が __行われることはなく__、結合された tuple を管理する __新しいオブジェクトが作られます__。

下記のプログラムでは、4 行目を実行すると、`a = a + (4, 5, 6)` と同じ処理が実行されるので、`a` にはその計算結果である `(1, 2, 3, 4, 5, 6)` を管理する __新しいオブジェクト__ の id が格納されます。そのため、3 行目と 7 行目で表示される `a` に格納されたオブジェクトの id が __異なります__。

また、2 行目で `b = a` を実行することで、`a` と `b` は 同じ tuple を管理するオブジェクトを __共有__ しますが、`a += (4, 5, 6)` を実行すると `a` __だけ__ が新しく作られたオブジェクトを参照するようになるので、5、6 行目で `a` と `b` を表示すると __異なる内容が表示__ されます。

```python
1  a = (1, 2, 3)
2  b = a
3  print(id(a))
4  a += (4, 5, 6)
5  print(a)
6  print(b)
7  print(id(a))
```

<details><summary><strong>行番号のないプログラム</strong></summary>

```diff_python
a = (1, 2, 3)
b = a
print(id(a))
a += (4, 5, 6)
print(a)
print(b)
print(id(a))
```
</details>

実行結果
```
2251181322304
(1, 2, 3, 4, 5, 6)
(1, 2, 3)
2251180188832
```

一方、下記のプログラムでは、4 行目を実行すると　`a` が参照するオブジェクトが管理する list の要素に `[4, 5, 6]` が __追加__ されるので、`a` が参照するオブジェクトが __変更されることはありません__。そのため、3 行目と 7 行目で表示される `a` に格納された __オブジェクトの id__ と、5、6 行目で表示される `a` と `b` の内容が __同じになります__。

```python
1  a = [1, 2, 3]
2  b = a
3  print(id(a))
4  a += [4, 5, 6]
5  print(a)
6  print(b)
7  print(id(a))
```

<details><summary><strong>行番号のないプログラム</strong></summary>

```diff_python
a = [1, 2, 3]
b = a
print(id(a))
a += [4, 5, 6]
print(a)
print(b)
print(id(a))
```
</details>

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

このように、__同じ `+=` 演算子__ が、tuple と list で __全く異なった処理__ が行われる点は、__間違いやすい__ ので注意して下さい。

なお、このノートで紹介した tuple のプログラムが、先ほどの __文字列型のデータ__ に関する補足で紹介したプログラムと __似ている__ と思った人がいるかもしれませんが、実際に __どちらも同じ種類の処理__ を行っているため、実行結果も同様の結果になります。
:::

### tuple の代入が疑似的な複製にならない例

tuple の要素には、__python の任意のデータ__ を記述することができます。下記のプログラムは、tuple の要素に __ミュータブル__ な list を記述しています。このように、__ミュータブルな値を持つ__ 要素を __含む__ tuple が代入された変数を、他の変数に代入した場合は、疑似的な複製には __なりません__。そのことを示します。

```python
a = ([1, 2], [3, 4])
b = a
```

`a` に代入された tuple の __要素__ は、tuple がイミュータブルなデータなので、下記のプログラムの 3 行目のように tuple の要素の値を変更しようとするとエラーが発生します。

```python
a = ([1, 2], [3, 4])
b = a
a[0] = [5, 6]
```
実行結果
```
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
Cell In[14], line 3
      1 a = ([1, 2], [3, 4])
      2 b = a
----> 3 a[0] = [5, 6]

TypeError: 'tuple' object does not support item assignment
```

しかし、`a` に代入された tuple の要素に __代入されている値__ は __ミュータブル__ な list なので、`a` の __要素の要素__ は、下記のプログラムの 3 行目のように値を代入して変更できてしまいます。また、3 行目を実行した結果、下記の実行結果が示すように `a` と `b` の値が同時に変化します。このことから、tuple はイミュータブルなデータで __あるにも関わらず__、`([1, 2], [3, 4])` という tuple を変数に代入した場合は、疑似的な複製ではなく、__データの共有__ が行われます。

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

下図は、上記のプログラムの 3 行目の処理を図示したものです。上記の説明の意味が良くわからない場合は、下図を見て 3 行目の処理でエラーが発生しない理由と、`a` と `b` でデータが共有されていることを確認して下さい。

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3524802/d4b16984-842f-b97b-a005-fc407f55a00a.png" width="800">


### イミュータブルなデータの代入が疑似的な複製になる条件

上記の例で、イミュータブルな tuple のデータが疑似的な複製に __ならない理由__ は、その tuple を構成するオブジェクトのデータの中に、__ミュータブルなデータが混じっている__ ためです。一つでもミュータブルなデータがあれば、そのデータは編集可能なので、「疑似的な複製」に求められる「__編集不可能性__」の __条件が満たされなくなります__。従って、__イミュータブルなデータの代入処理__ が、__疑似的な複製__ になる __条件__ は、以下のようになります。

:::note warn
データを __構成__ する、__全ての__ オブジェクトが管理する __データがイミュータブル__ な場合、そのデータが代入された変数を、他の変数に代入すると、__疑似的な複製が行われる__。
:::

__単一データ型のイミュータブルなデータ__ は、データを構成するオブジェクトが __1 つしかない__ ので、__この条件を満たします__。一方、複合データ型が上記の条件を満たすかどうかを判断するためには、そのデータを構成する全てのオブジェクトのデータが全てイミュータブルであるかどうかを __確認する__ 必要があります。

例えば、`(1, 2, 3)` はその条件を満たすので、下記のプログラムのように、このデータが代入された変数を、他の変数に代入すると、疑似的な複製が行われます。

```python
a = (1, 2, 3)
b = a
print(a)
print(b)
a = (4, 5, 6)
print(a)
print(b)
```
実行結果
```
(1, 2, 3)
(1, 2, 3)
(4, 5, 6)
(1, 2, 3)
```

## まとめ

ここまでの説明で、下記の説明が不正確な理由を説明しました。

:::note alert
変数に別の変数を直接代入した際に、下記のような処理が行われる。

* イミュータブル なデータを代入すると、データが __複製__ される
* ミュータブル なデータを代入すると、データが __共有__ される
:::

正しくは、以下のように理解して下さい。

:::note warn
変数に別の変数を直接代入した際に、下記のような処理が行われる。

* __ミュータブル__ であろうと __イミュータブル__ であろうと、データが __共有__ される

* データを構成する、__全ての__ オブジェクトが管理する __データがイミュータブル__ なデータを代入した場合は、一見すると複製が行われている __ように見える__、__疑似的な複製__ が行われる。__疑似的な複製__ は、__複製元と複製先__ のデータに対して __編集を行うことができない__ という性質を持つ

__大事なこと__ なので繰り返しますが、__疑似的な複製__ は、あくまで複製が行われているように __見えるだけ__ で、__実際には共有__ が行われます。__本当の意味での複製は行われない__ 点に注意して下さい。
:::

「データを構成する、全てのオブジェクトが管理するデータがイミュータブルなデータ」という表現は長いので、以後はそのようなデータを「__疑似的な複製が行われるデータ__」と表記することにします。

## 部分的な共有

説明を後回しにしていた、今回の記事の前半の「複合データ型のリテラルの中に、変数のみが記述されているものがある場合」で言及した、「__部分的な共有__」について説明します。

下記のプログラムの 2 行目では、`b` という変数に、複合データ型のリテラルの中に、`a` という __変数のみが記述__ されているデータを代入しています。

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

このような場合は、以下のような __非対称__ で __部分的な__ データの共有が行われます。

* 代入先の変数 `b` は、リテラルの内部に記述された変数 `a` のデータを __全て共有__ する
* リテラルの内部に記述された変数 `a` は、代入先の変数 `b` の __一部を共有__ する
* リテラルの内部に記述された変数 `a` と、代入先の `b[1]` の要素が __同じデータを共有__ する

### 部分的な共有の確認

このことは、プログラムの実行結果から確認することができます。

* `b` の値ある `[3, [1, 2]]` には、`a` の値である `[1, 2]` の全てのデータが表示されているので、`b` は `a` のデータを全て共有している
* `a` 値である `[1, 2]` には、`b` の値である `[3, [1, 2]]` の一部のデータしか表示されていないので、`a` は `b` の一部のデータを共有している
* `a` と `b[1]` の値は同じ `[1, 2]` なので、`a` と `b[1]` は同じデータを共有している

また、上記を図で確認することもできます。下図は、上記のプログラムの実行結果を図示したものです。図の __水色の枠内__ のオブジェクトが __`a` の値を構成するオブジェクト__ を、__紫色の枠内__ のオブジェクトが、__`b` の値を構成するオブジェクト__ を表します。図を見て、「`b` は `a` を全て共有する」、「`a` は `b` の一部を共有する」、「`a` と `b[1]` が同じデータを共有する」ことを確認して下さい。

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3524802/2c505e1e-b6e9-129f-59f1-c651ecc379c4.png" width="400">

上記のプログラムに __続けて__、下記のプログラムの 3 行目を実行することでも、「`b` は `a` を全て共有する」ことを確認できます。`b` は `a` のデータを完全に共有しているので、下記のプログラムの 3 行目のように、`a` の要素の値を変更すると、`b` の値も変化します。

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

上記のプログラムに __続けて__、下記のプログラムの 4 行目を実行することでも、「`a` は `b` の一部を共有する」ことを確認できます。`a` は `b[0]` を __共有しない__ ので、下記のプログラムの 4 行目のように、`b[0]` の要素の値を変更しても、`a` の値は変化しません。

```python
1  a = [1, 2]
2  b = [3, a]
3  a[0] = 5
4  b[0] = 7
5  print(a)
6  print(b)
```

<details><summary><strong>行番号のないプログラム</strong></summary>

```diff_python
a = [1, 2]
b = [3, a]
a[0] = 5
b[0] = 7
print(a)
print(b)
```
</details>

実行結果
```
[5, 2]
[7, [5, 2]]
```

### 部分的な共有で行われる処理と疑似的な複製

`b = [3, a]` で行われる処理は、要素が 2 つある list を作成した後で、`b[0] = 3` と `b[1] = a` のような __代入処理__ が行われるとみなすことができます。そのため、`a` に数値型のように、__疑似的な複製が行われるデータ__ が代入されている場合は、`b[1]` の値は `a` の値から __疑似的な複製__ が行われたものになります。

下記のプログラムでそのことを確かめることができます。下記のプログラムでは、1 行目で疑似的な複製が行われる数値型のデータを `a` に代入しています。そのため、2 行目を実行すると、`a` の __疑似的な複製__ が `b[1]` に代入されます。そのため、5 行目のように `a` に別の値を代入しても、`b[1]` の値は __変化しません__。そのことを、実行結果から確認して下さい。

```python
1  a = 1
2  b = [3, a]
3  print(a)
4  print(b)
5  a = 5
6  print(a)
7  print(b)
```

<details><summary><strong>行番号のないプログラム</strong></summary>

```diff_python
a = 1
b = [3, a]
print(a)
print(b)
a = 5
print(a)
print(b)
```
</details>

実行結果
```
1
[3, 1]
5
[3, 1]
```

### 複雑なリテラルが記述されている場合

複合データ型のリテラルに、下記のプログラムのように複数の変数が記述されていたり、記述されている変数に代入されたリテラルの値の中にも変数が記述されているような __複雑な場合__ でも、同様の考え方で、__一つ一つの代入処理を順番に考えて__ いくことで、変数どうしの関係を理解することができます。下記のプログラムの 3、4 行目の処理は、以下のように考えると良いでしょう

1. `a = [1, 2]` と `c = [5, a]` から `c` に `[5, [1, 2]]` が代入される
2. `b = [3, 4]` と `d = [b, c]` から `d` に `[[3, 4], c]` が代入される
3. `c = [5, [1, 2]]` と `d = [[3, 4], c]` から `d` に `[[3, 4], [5, [1, 2]]]` が代入される

```python
a = [1, 2]
b = [3, 4]
c = [5, a]
d = [b, c]
print(d)
```
実行結果
```
[[3, 4], [5, [1, 2]]]
```

頭の中だけで考えると混乱するかもしれませんので、分からなくなったら図を書くことをお勧めします。

### まとめ

部分的な共有についてまとめると以下のようになります。

:::note warn
複合データ型のリテラルの中に、変数のみが記述されているものがあるデータがを変数に代入した場合は、以下のような __非対称__ で __部分的__ な __データの共有__ が行われる。

* 代入先の変数は、リテラルの内部に記述された変数のデータを __全て共有__ する
* リテラルの内部に記述された変数は、代入先の変数の __一部を共有__ する
* リテラルの内部に記述された変数と、代入先の要素が __同じデータを共有__ する

その際に、`代入先の要素 = リテラルの内部に記述された変数` のような処理が行われる。そのため、部分的な複製が行われるかどうかは、__一般的な代入処理と同じ方法__ で判断することができる。
:::

## 完全な複製

Python の代入処理は、データを管理するオブジェクトを __共有__ する処理なので、データの __完全な複製__ は、Python の __代入文だけで行うことはできません__。

Python ではデータの完全な複製を行う方法がいくつか用意されています。list の場合は list を表すデータの後ろに `.copy()` を記述することで、list を完全に複製したデータを作成することができます。下記のプログラムは、2 行目で `a.copy()` を記述することで、`a` の値を完全に複製したデータを `b` に代入しています。そのため、5 行目で `a` の 0 番の要素に `5` を代入しても、実行結果からわかるように `b` の値は変化しません。

```python
1  a = [1, 2, 3]
2  b = a.copy()
3  print(a)
4  print(b)
5  a[0] = 5
6  print(a)
7  print(b)
```

<details><summary><strong>行番号のないプログラム</strong></summary>

```diff_python
a = [1, 2, 3]
b = a.copy()
print(a)
print(b)
a[0] = 5
print(a)
print(b)
```
</details>

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

他にもいくつか完全な複製を行う方法がありますが、完全な複製を行う際にはいくつか気をつけなければならない点があります。例えば、先ほど紹介した `.copy()` では、list の完全な複製を __うまく行えない__ 場合があります。それらについて説明すると長くなるので、完全な複製については完全な複製を行う必要が出てきた時点で説明することにします。

:::note
以上が Python の変数と代入処理に関する説明ですが、今回の記事でで説明した処理は複雑で、理解しがたい所が多いと思います。筆者もこの記事を書いている最中に、何度も頭が混乱しました。もしかすると、今も少し混乱していて、この記事で書いた内容が間違っているかもしれないと心配になるくらいです（実際に間違っているかもしれません。その場合は指摘していただけると大変助かります）。特に、頭の中だけで考えると訳が分からなくなりがちなので、分からなくなったら、__図を書くこと__ をお勧めします。

また、今回の記事があまり理解できなくても悲観することはありません。プログラミングの技術が上達すればだんだんとわかるようになると思いますので、今回の記事の意味が理解できるようになったと感じたときにもう一度読み返してみて下さい。
:::

# その他の補足説明

今回の記事に関連する、その他の補足説明をいくつか行います。

## オブジェクトの使いまわし

以前の記事で、Python では `1` や `" "` などのリテラルが記述された式が実行されるたびに、そのデータを管理する __新しいオブジェクトが作成される__ という説明をしました。

しかし、実際には Python では、同じデータを管理するオブジェクトが __過去に作成__ されていた場合、新しいオブジェクトを作成する代わりに、そのオブジェクトが __使いまわされる場合があります__。オブジェクトが使いまわされる理由としては、プログラムの中の計算式で __良く使われる__、数値型や文字列型のデータが記述された文を実行するたびに __新しいオブジェクトを作る__ のは __効率が悪い__ ためです。

この補足説明では、どのような場合にオブジェクトが __使いまわされる可能性__ があり、どのような場合に使いまわしが __決して行われない__ かについて説明します。

下記のプログラムはオブジェクトが使いまわされる例です。なお、下記の実行結果は筆者のコンピューターで下記のプログラムを実行した場合のものです。コンピューターの OS や Python のバージョンなどによっては、使いまわされない場合があるかもしれません。

1 行目と 2 行目で同じ数値型の `1` が 2 回記述されていますが、実行結果からオブジェクトの id が等しいので __オブジェクトが使いまわされている__ ことがわかります。

3 行目では、list の要素に同じ数値型の `2` が代入された 2 つの list を結合したデータを `a` に代入しています。5、6 行目の実行結果から、`a` の 2 つの要素が、`2` を管理する __同じオブジェクトを参照__ しており、ここでもオブジェクトが __使いまわされている__ ことがわかります。

```python
1  print(id(1))
2  print(id(1))
3  a = [2] + [2]
4  print(a)
5  print(id(a[0])) 
6  print(id(a[1])) 
```

<details><summary><strong>行番号のないプログラム</strong></summary>

```diff_python
print(id(1))
print(id(1))
a = [2] + [2]
print(a)
print(id(a[0])) 
print(id(a[1]))
```
</details>

実行結果
```
140731432669992
140731432669992
[2, 2]
140731432670024
140731432670024
```

このような使いまわしが行われる __可能性__ があるのは、本記事で説明した、__疑似的な複製が行われるデータ__ に __限ります__。具体的には、「データを構成する、__全ての__ オブジェクトが管理する __データがイミュータブル__」なデータです。__それ以外のデータ__ では、このようなオブジェクトの使いまわしは   __決して行われません__。

__疑似的な複製が行われるデータ__ を管理するオブジェクトが使いまわされる可能性があるのは、そのようなデータは、疑似的な複製の __編集不可能性__ の性質から、__編集することができない__ からです。決して編集することができないデータであれば、そのデータを複製しても、共有して使いまわしても、__そのデータを使う側__ からみると __実質的な違いはない__ ため、その後のプログラムの __処理に影響を及ぼさない__ からです。

なお、__疑似的な複製が行われるデータ__ を管理するオブジェクトが必ず使いまわされるとは限りません。例えば、下記のプログラムは 1 行目と 2 行目で同じ数値型の `1.1` が 2 回記述されていますが、筆者のコンピュータで実行すると、実行結果のようにオブジェクトの id が異なるので、オブジェクトが使いまわされて __いない__ ことがわかります。

```python
print(id(1.1))
print(id(1.1))
```
実行結果
```
2251180241456
2251165610640
```

具体的にどのような場合に使いまわしが行われるかについては調べても良くわかりませんでしたが、その法則を __知る必要はありません__。オブジェクトの使いまわしは、プログラムの処理の効率をよくするために __Python が自動的に行う__ 処理です。オブジェクトが使いまわされていても、いなくても、プログラムの処理に __影響は与えない__ ので、プログラムを記述する際に、オブジェクトの使いまわしについて __意識する必要はありません__[^8]。従って、プログラムの __処理を理解する__ 際に、オブジェクトの __使いまわしは行われないと考えても問題はない__ でしょう。

上記をまとめると以下のようになります。

:::note warn
リテラルが記述された文を実行する際に、そのリテラルを管理するオブジェクトが使いまわされるかどうかは、以下の条件で決まる。

* __疑似的な複製が行われる__ データを管理するオブジェクトは、__使いまわされる可能性がある__。ただし、この条件が満たされている場合でも使いまわされるとは __限らない__
* それ以外のデータを管理するオブジェクトは、__決して使いまわされない__

オブジェクトの使いまわしは、プログラムの処理の効率をよくするために __Python が自動的に行う__ ものなので、プログラムで行われる __処理を理解する__ 上では、オブジェクトの __使いまわしは行われないと考えても良い__
:::

## 変数とオブジェクトの図示

本記事では図を使って変数やオブジェクトの構造を説明してきましたが、自分でこのような図を書くのは大変です。そこで、Python のプログラムを記述すると、そのプログラムを実行した際の __変数とオブジェクトを図示__ してくれるウェブページ（英語のページです）を紹介します。

https://pythontutor.com/

このウェブページは以下の手順で使用します。

1. ページ内の「Start coding now in Python, JavaScript, C, C++, and Java」にある、「Python」のリンクをクリックする
2. テキストボックスの下にある「inline primitives, don`t nest objects [default]」のメニューをクリックして、「render all objects on the heap (Python/Java)」を選択する。この操作を行わないと、数値型などの、単一データ型のオブジェクトが図示されません
3. テキストボックスにプログラムを記述し、「Visualize Execution」のボタンをクリックする

下図は、テキストボックスに前回までの記事で紹介した プログラム B を入力してボタンをクリックした場合の図です。

```python
a = [1, 2, 3]
b = a
a[0] = 5
print(a)
print(b)
```

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3524802/a467888a-ae7c-2477-a01d-174ffbdef039.png" width="350">

このツールは、この画面でプログラムを __1 行ずつ実行しながら__、変数とオブジェクトの関係を図示します。図の記号やボタンは以下の意味を持ちます。なお、上記の図はプログラムを実行する前の状態なので、緑色の矢印は表示されていません。

| 記号やボタン | 意味 |
|:--|:--|
| 赤い矢印  | 次に実行するプログラムの行  |
| 緑の矢印  | 直前に実行されたプログラムの行  |
| Edit this code  | プログラムを編集する画面に戻る  |
| <<First  | プログラムを実行開始する前の状態に戻す  |
| <Prev  | プログラムの処理を 1 つ前に戻す  |
| Next>  | 赤い矢印の行を実行する  |
| Last>>  |  プログラムの処理を最後まで行う |

上記の図で「Next>」ボタンをクリックすると 1 行目のプログラムが実行され、下図のような画面が表示されます。

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3524802/c35f7acd-1838-4afa-1a45-a91c9bc579f4.png" width="600">

図の左側では、直前で実行された 1 行目に緑の矢印が表示され、次に実行する 2 行目に赤い矢印が表示されます。

図の右側には、1 行目を実行した時点での、変数とオブジェクトが図示されます。本記事の図とはオブジェクトや変数の表記方法が若干異なりますが、よく見れば `a = [1, 2, 3]` が図示されていることがわかると思います。なお、本記事の図と大きく異なる点は、このツールでは、「オブジェクトの id が表示されない」点と、「オブジェクトの上にオブジェクトが管理するデータのデータ型が表示される」点です。

図の右側の上部には、`print` によって表示される内容が表示されます。1 行目には `print` が記述されていないので、図のこの部分は空欄になっています。

下図は、「Next>」を複数回クリックして、プログラムを最後まで実行した際の、画面の右に表示される内容を表しています。

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3524802/300cc54e-50ed-460e-f19c-fc9ff2d7cf6b.png" width="350">

別のプログラムを実行したい場合は、左の「Edit this code」をクリックして下さい。

このツールでは、「Next>」をクリックするたびに、プログラムが 1 行ずつ実行され、右に表示される変数とオブジェクトの関係の図がそれに伴い更新されていくので、プログラムの __処理の流れ__ と、それに伴い __変数とオブジェクト__ がどのように __変化していくかを把握__ するには __便利なツール__ だと思います。

ただし、プログラムが扱うデータが増えると、どうしても右の図が __大きく複雑__ になって、__全体を把握しづらく__ なるという __欠点__ があります。そのような場合は、__自分で図を描いたほうが良いかもしれません__。

そのような例を実際に確認してみたい人は、10 行 10 列 の初期化されたゲーム盤を作成する `board = [[" "] * 10 for x in range(10)]` というプログラムをこのツールで入力して、「Last>>」ボタンをクリックしてみて下さい。

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

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

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

# 次回の記事

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

# 更新履歴

| 更新日時 | 更新内容 |
|:--|:--|
| 2023/9/21 | 「限定的な複製」を「疑似的な複製」に修正しました |
| 2023/9/20 | 変数に別の変数を直接代入した際に行われる処理の説明を修正しました |
| 2023/9/20  | 「内容の同一性」を「同値性」に修正しました  |

[^1]:`1`、`"abc"`、`[1, 2, 3]` などのように、プログラム内に直接記述されたデータの事です。詳細は「その 7」の記事を参照して下さい
[^2]:immutable。「変わらない」、「不変の」という意味の英単語
[^3]:mutable。「変わりやすい」、「変更できる」という意味の英単語
[^4]:整数を表す integer の略
[^5]:浮動小数点数を表す floating point number から付けられています
[^6]:複素数を表す complex number から付けられています
[^7]:「組」を表す英単語。ここでいう組とは「二つ以上を取り合わせたひとまとまりのもの」という意味を表す
[^8]:意識する必要があるとすれば、本記事のように、__オブジェクトの仕組みを説明する__ 必要がある場合くらいでしょう