Pythonで〇×ゲームのAIを一から作成する　その20　クラスとインスタンスの仕組み

# 目次と前回の記事

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

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

# 実装の進捗状況と前回までのおさらい

## 〇×ゲームの仕様と進捗状況

1. `正方形で区切られた 3 x 3 の 2 次元のゲーム盤上でゲームを行う`
2. `ゲーム開始時には、ゲーム盤の全てのマスは空になっている`
3. `2 人のプレイヤーが遊ぶゲームであり、一人は 〇 を、もう一人は × のマークを受け持つ`
4. __2 人のプレイヤーは、交互に空いている好きなマスに自分のマークを 1 つ置く__
5. 先手は 〇 のプレイヤーである
6. プレイヤーがマークを置いた結果、縦、横、斜めのいずれかの一直線の 3 マスに同じマークが並んだ場合、そのマークのプレイヤーの勝利とし、ゲームが終了する
7. すべてのマスが埋まった時にゲームの決着がついていない場合は引き分けとする

仕様の進捗状況は、以下のように表記します。

* 実装が完了した部分を `背景が灰色の長方形` で記述する
* 実装の一部が完了した部分を、__太字__ で記述する

## これまでに作成したモジュール

以下のリンクから、これまでに作成したモジュールを見ることができます。

https://github.com/ysgeso/marubatsu/blob/master/020/marubatsu.py

## 前回までのおさらい

前回の記事では、オブジェクト指向プログラミングについて説明し、これまでに作成した関数を `Marubatsu` というクラスで定義しなおしました。

# クラスとインスタンスの仕組み

前回の記事で、__属性__、__メソッド__、__クラス__、__インスタンス__ などの、オブジェクト指向プログラミングの基本的な用語と簡単な使い方について説明しました。

前回の記事を読んだだけでは、多くの方はクラスとインスタンスの __違い__ や、それらが __異なったもの__ として __存在する理由__ について理解できないのではないかと思います。

オブジェクト指向プログラミングを行うためには、__クラスとインスタンスに関する仕組み__ について理解する必要があります。また、これらの仕組みの中で、クラスとインスタンスの __名前空間__ の仕組みについて理解することが重要です。今回の記事ではそれらについて説明します。

## dict

Python の __名前空間__ は、list と似た性質を持つ __dict__ という __データ型__ を使って実現されています。

:::note
dict の詳細については下記のリンク先を参照して下さい。
:::

https://docs.python.org/ja/3/library/stdtypes.html?highlight=dict#mapping-types-dict

dict は日本語では __辞書（dictionary）型__ と呼ばれる、その __中に複数のデータを持つ__ ことが出来る __[複合データ型](https://qiita.com/ysgeso/items/9efd60c7a28381e6cbf7#%E8%A4%87%E5%90%88%E3%83%87%E3%83%BC%E3%82%BF%E5%9E%8B%E3%81%AE%E5%AE%9A%E7%BE%A9)__ のデータで、__list とよく似た性質__ を持っているので、list の性質と対応させながら説明します。

下記の表は、dict と list の主な違いをまとめたものです。

| 格納するデータの性質 | list | dict |
|:--|:--|:--|
| __名称__  | 要素  |  値（value） |
| __識別子の名称__  | インデックス  |  キー（key） |
| __識別子のデータ型__  | 整数型のみ  | 文字列型など  |
| __記述方法__ | `listのデータ[インデックス]` | `dictのデータ[キー]` |
| __リテラルで囲う記号__  | `[` と `]`  | `{` と `}`  |
| __リテラルでの記述方法__  | データをそのまま記述する  | `キー: 値`  |

dict が格納するデータの __識別子__ の事を、__キー__（key）と呼びます。__list__ の識別子である __インデックス__ が __整数しか__ 使うことが __出来ない__ のと異なり、__dict__ の識別子である __キー__ には __文字列型__、数値型、tuple などを使うことが出来ますが、list、dict をキーとして利用することはできません（エラーが発生します）。dict のキーとしては __文字列型__ のデータが __良く使われます__。

:::note
dict のように、__文字列など__ の __整数以外__ のデータを __識別子__ として、複数のデータを格納できるようなデータ型の事を、__連想配列__ と呼ぶ場合があります。連想配列は、ハッシュテーブルという仕組みを使って実現される場合が多いので、__ハッシュ__ と呼ぶ場合もあります。

説明が長くなるので詳細は省略しますが、dict のキーとして利用できるのは、__ハッシュ可能__ なデータです。数値型、文字列型、論理型、None 型、tuple など、__[イミュータブル](https://qiita.com/ysgeso/items/cc9e67c16b0814c8bc2e#%E3%83%9F%E3%83%A5%E3%83%BC%E3%82%BF%E3%83%96%E3%83%AB%E3%81%A8%E3%82%A4%E3%83%9F%E3%83%A5%E3%83%BC%E3%82%BF%E3%83%96%E3%83%AB) なデータ__ は __一部の例外[^1] を除いて__ ハッシュ可能なデータです。ハッシュ可能なデータについての詳細は下記のリンク先を参照して下さい。
:::

https://docs.python.org/ja/3/glossary.html#term-hashable

dict が格納するデータの事を __dict の値__（value）と呼び、キーに対応するデータの事を、__キーの値__ と呼びます。キーの値は、__`dictのデータ[キー]`__ のように記述します。

list の要素と同様に、プログラムに __記述した dict の値__ は __変数と同様の性質__ を持ち、__任意のデータを代入__ したり、__式の中で記述__ することで dict の値を取り出して利用することが出来ます。

__dict のリテラル__ は、下記のように、__半角の `{` と `}` の間__ に __キーと値のペア__ を、間に __半角の `:`__（コロン）を挟んで __`キー: 値`__ のように記述します。__複数の__ キーと値のペアを記述する場合は、__半角の `,` で区切り__ ます。また、list のリテラルの場合と同様に、`,` の後などで __改行__ することが出来ます。

__`{}`__ は、__値を一つも持たない dict__ を表し、そのような dict のことを __空の dict__ と呼びます。

```python
{ キー1: 値1, キー2: 値2,  (必要なだけ キー: 値 を記述できる) }
```

:::note warn
list と dict の主な違いは、「いくつかの用語」、「__識別子が整数であるかどうか__」、「__リテラルで囲う記号の違い__」などで、それ以外の性質は __多くの部分で共通__ します。
:::

下記のプログラムは、1 行目で、2 つの値を持つ dict を `a` に代入し、2、3 行目で 2 つのキーの値を、4 行目で dict そのものを `print` で表示しています。実行結果からわかるように、dict を `print` で表示すると、dict の __リテラルと同じ形式__ で表示が行われます。

```python
a = { "fruits": "apple", "price": 1000 }
print(a["fruits"])
print(a["price"])
print(a)
```
実行結果
```
apple
1000
{'fruits': 'apple', 'price': 1000}
```

:::note
dict の __キー__ と、オブジェクトの __属性名__ はどちらも __文字列を識別子__ にできるので、dict のキーの値を `オブジェクト.キー` のように記述できると思う人がいるかもしれませんが、そのような記述をすると下記のプログラムのように __エラーになる__ 点に注意して下さい。

```python
print(a.fruits)
```
実行結果
```
---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
c:\Users\ys\ai\marubatsu\020\marubatsu.ipynb セル 2 line 1
----> 1 print(a.fruits)

AttributeError: 'dict' object has no attribute 'fruits'
```

詳細は省略しますが、エラーが発生する原因は、dict のキーが、この後で説明するオブジェクトの名前空間と __異なる方法__ で、__キーと値の対応づけ__ を行っているからです。
:::

### dict への値の追加

list に要素を __追加__ するためには、`append` メソッドなどを使う必要がありましたが、dict の場合は、__`[]` の中にキーを記述__ し、__代入文__ を使って __直接__ dict に __値を追加__ することが出来ます。

下記のプログラムは、上記のプログラムの続きで、__代入文__ を使って `"color"` という __キーの値__ に `"red"` という文字列を代入しています。実行結果から、dict に新しい `"color"` というキーとその値が __追加されている__ ことを確認することが出来ます。

```python
a["color"] = "red"
print(a["color"])
print(a)
```
実行結果
```
red
{'fruits': 'apple', 'price': 1000, 'color': 'red'}
```

### for 文による dict の繰り返し

dict は list と同様に __反復可能オブジェクト__ なので、__for 文による繰り返し処理__ で利用できます。

#### for 文で dict のキーを取り出す繰り返し

for 文の反復可能オブジェクトに dict を記述した場合は、繰り返しのたびに、dict の __キー__ が取り出されて for 文の __変数に代入__ されます。下記は、先程のプログラムの続きで、`a` に代入された dict の __キーを表示__ するプログラムです。

取り出されるキーの順番は、__dict に値を追加した順番と同じ__ [^2]です。dict のリテラルを記述した場合は、空の dict に対して、__先頭__ のキーと値のペア __から順番__ に dict に値が追加されます。

先程のプログラムは、リテラルで `fruits`、`price` のキーと値が、その後の代入文で `color` のキーと値が dict に追加されたので、下記のプログラムは __その順番__ で __キーが表示__ されます。

```python
for key in a:
    print(key)
```
実行結果
```
fruits
price
color
```

:::note
dict の __キーの一覧__ は、下記のプログラムのように、__dict のキーの一覧__ を返す、__`keys`__ という __メソッド__ を使って取得することも出来ます。実行結果に表示される、dict_keys の直後の `()` の中に __list のリテラルと同じ形式__ で、__dict のキーの一覧__ が表示されます。

```python
print(a.keys())
```
実行結果
```
dict_keys(['fruits', 'price', 'color'])
```

for 文を使って dict のキーの一覧を利用する場合に `keys` メソッドを利用する意味はないでしょう。`keys` メソッドの具体的な利用例については必要になった時点で紹介します。

__dict_keys__ は、この後で説明する `values` メソッドの返り値である __dict_values と同じ性質を持つ__ ので、詳細はこの後の説明を見て下さい。
:::

#### for 文で dict の値を取り出す繰り返し

for 文で dict の __値__ を利用したい場合は、以下の 2 つの方法があります。

一つ目の方法は、先ほど説明した dict から __キーを取り出す繰り返しのブロック__ の中で、下記のプログラムの 2 行目のように、取り出した __キーを使って dict の値を記述__ する方法です。

```python
for key in a:
    print(a[key])
```
実行結果
```
orange
1000
red
```

もう一つの方法は __dict の値の一覧__ を返す、__`values`__ という __メソッド__ を利用する方法です。

下記のプログラムは、`a.values()` を表示することで、`a` に代入された __dict の値__ を __要素として持つ dict_values__ という __複合データ型__ のデータを表示しています。表示結果の dict_values の `()` の中に記述されたデータは、list のリテラルと同じ形式で __dict の値__ が表示されます。

```python
print(a.values())
```
実行結果
```
dict_values(['orange', 1000, 'red'])
```

dict_values は __反復可能オブジェクト__ なので、下記のプログラムのように、__for 文の反復可能オブジェクト__ として記述することで、繰り返しの中で __dict の値__ を __先頭から順__ に取り出して for 文の変数に代入することが出来ます。

```python
for value in a.values():
    print(value)
```
実行結果
```
orange
1000
red
```

:::note
__for 文の反復可能オフジェクトとして利用する__ 場合は、dict_values は __list と同じ性質を持つ__ と考えても構いませんが、__それ以外の場合__ で dict_values を利用する場合は __注意が必要__ です。具体的には dict_values に対して、list のように `[]` とインデックスを使ってデータを代入したり、データを取り出すことは __できません__。下記のプログラムを実行するとエラーが発生します。

```python
print(a.values()[0])
```
実行結果
```
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
c:\Users\ys\ai\marubatsu\020\marubatsu.ipynb セル 9 line 1
----> 1 print(a.values()[0])

TypeError: 'dict_values' object is not subscriptable
```
上記のエラーメッセージは、以下のような意味を持ちます。

* __TypeError__
データ型（Type）に関するエラー
* __'dict_values' object is not subscriptable__
dict_values 型のオブジェクトは、`[]` を使ってその中のデータを参照する（subscript[^3]）ことはできない（is not able）


dict_values を __list のように利用__ したい場合は、list の __[組み込み型のクラス](https://qiita.com/ysgeso/items/a189461ae32a6531ff7f#%E7%B5%84%E3%81%BF%E8%BE%BC%E3%81%BF%E5%9E%8B%E3%81%AE%E3%82%AF%E3%83%A9%E3%82%B9%E3%81%A8%E3%82%A4%E3%83%B3%E3%82%B9%E3%82%BF%E3%83%B3%E3%82%B9) である `list`__ を利用します。`list` に __実引数__ を記述して呼び出すと、実引数の値を __list に変換した値を返す__ という処理が行われるので、下記のプログラムのように記述することで、__dict_values__ を __list に変換__ することができます。なお、先ほどのノートで説明した `dict_keys` も同様の方法で list に変換することが出来ます。

組み込み型の変換については[前回の記事](https://qiita.com/ysgeso/items/a189461ae32a6531ff7f#%E7%B5%84%E3%81%BF%E8%BE%BC%E3%81%BF%E5%9E%8B%E3%81%AE%E5%A4%89%E6%8F%9B)を参照して下さい。

```python
values = list(a.values())
print(values)
print(values[0])
```
実行結果
```
['apple', 1000, 'red']
apple
```
:::

#### for 文で dict のキーと値の両方を取り出す繰り返し

dict には、dict の __キーと値__ を __要素__ として持つ __tuple[^4] の一覧__ を返す、__`items` メソッド__ があります。

下記のプログラムは、`a.items()` を表示することで、`a` の __キーと値__ を __要素__ として持つ __tuple の一覧__ を持つ __dict_items__ という複合データ型のデータを表示しています。__dict_items__ は __dict_keys__ や __dict_values__ と __同様の性質を持つデータ型__ です。

```python
print(a.items())
```
実行結果
```
dict_items([('fruits', 'orange'), ('price', 1000), ('color', 'red')])
```

dict_items も __反復可能オブジェクト__ なので、下記のプログラムのように、for 文の反復可能オブジェクトとして記述することで、繰り返しの中で先頭の tuple から順に取り出して for 文の変数に代入することが出来ます。下記のプログラムは、dict の __キーと値のペア__ を要素として持つ tuple を先頭から順に表示します。

```python
for item in a.items():
    print(item)
```
実行結果
```
('fruits', 'orange')
('price', 1000)
('color', 'red')
```

__tuple の要素__ は __list と同じ方法__ で __`[]` とインデックス__ を記述して参照することが出来ます。`item` に代入された tuple は、__0 番__ の要素に __キー__ が、__1 番__ の要素に __値__ が代入されているので、下記のようなプログラムを記述することで、キーと値を個別に表示することが出来ます。

```python
for item in a.items():
    print("key:", item[0], ", value:", item[1])
```
実行結果
```
key: fruits , value: apple
key: price , value: 1000
key: color , value: red
```

Python では、下記のように記述することで、tuple の __要素__ を __別々の変数に代入__ する処理を __1 行の文で記述__ することが出来ます。下記のプログラムの 1 行目では、`x` に tuple の 0 番の要素の値を、`y` に tuple の 1 番の要素の値を代入する処理が行われます。

```python
(x, y) = (1, 2)
print(x, y)
```
実行結果
```
1 2
```

従って、先程のプログラムは、下記のように記述することができます。先ほどのプログラムと下記のプログラムは __全く同じ処理__ を行うプログラムなのでどちらを記述しても問題はありませんが、下記のプログラムの方が、3 行目の処理の __見た目がわかりやすくなる__ という利点があります。

```python
for item in a.items():
    (key, value) = item
    print("key:", key, ", value:", value)
```

<details><summary><strong>修正箇所</strong></summary>

```diff_python
for item in a.items():
+   (key, value) = item
-   print("key:", item[0], ", value:", item[1])
+   print("key:", key, ", value:", value)
```
</details>

実行結果
```
key: fruits , value: apple
key: price , value: 1000
key: color , value: red
```

さらに、上記のプログラムの 2 行目の処理は、下記のプログラムの 1 行目のように、for 文の __変数の部分にまとめる__ ことが出来ます。このような記述は実際に良く行われるので意味を覚えておくことをお勧めします。

```python
for (key, value) in a.items():
    print("key:", key, ", value:", value)
```

<details><summary><strong>修正箇所</strong></summary>

```diff_python
- for item in a.items():
+ for (key, value) in a.items():
-     (key, value) = item
      print("key:", key, ", value:", value)
```
</details>


実行結果
```
key: fruits , value: apple
key: price , value: 1000
key: color , value: red
```

:::note
__tuple の `()`__ は __省略__ することが出来ます。下記の 2 つのプログラムは、上記の 2 つのプログラムと __同じ処理__ を行うプログラムです。実行結果は変わらないので省略します。
```python
x, y = 1, 2
print(x, y)
```

<details><summary><strong>修正箇所</strong></summary>

```diff_python
- (x, y) = (1, 2)
+ x, y = 1, 2
print(x, y)

```
</details>

```python
for key, value in a.items():
    print("key:", key, ", value:", value)
```

<details><summary><strong>修正箇所</strong></summary>

```diff_python
- for (key, value) in a.items():
+ for key, value in a.items():
    print("key:", key, ", value:", value)
```
</details>

ただし、以下の場合は、__`()` の省略はできません__。
* __空の tuple__ の場合は __`()` を省略できない__
* `()` を記述しないと、プログラムの __意味があいまいになる場合__

曖昧になる例として、__関数の実引数__ に __tuple のリテラルを記述__ する場合が挙げられます。

例えば、__`print(1, 2)`__ と記述した時に、__2 つの実引数 `1` と `2`__ を記述したのか、__1 つの tuple の実引数 `(1, 2)`__ を記述したのかがあいまいになります。このような場合は、__`()` を省略することはできない__ ので、__2 つの実引数 `1` と `2` を記述__ したことになります。

__1 つの tuple のリテラル__ である __`(1, 2)` を実引数に記述__ する場合は、__`print((1, 2))`__ のように記述する必要があります。このような記述は __慣れるまで分かりづらく感じられる__ かもしれませんが、実際に __良く使われる記述__ なので覚えて下さい。

上記の 2 つを実行する下記のプログラムの実行結果から、上記について確認して下さい。

```python
print(1, 2)
print((1, 2))
```
実行結果
```
1 2
(1, 2)
```

__実際に__、tuple の `()` は __良く省略される__ ので、以降は __省略してもわかりづらくならない場合__ は __省略する__ こにします。
:::

## dict による名前空間の管理

dict は、__識別子__ である __キー__ から、__キーの値__ を管理するオブジェクトを __対応づける__ というデータ構造です。キーには __文字列型__ のデータを __指定__ することが出来るので、__dict__ を __名前空間を表すデータ__ としてそのまま __利用する__ ことが出来ます。

:::note warn
__実際に__、__Python__ では __名前空間を dict で管理__ しています。
:::

### グローバル名前空間とローカル名前空間を管理する dict

グローバル名前空間とローカル名前空間を管理する dict は、組み込み関数 `globals` と `locals` を使うことで取得することが出来ます。

下記のプログラムは、`globals()` を使って __グローバル名前空間を管理__ する __dict を表示__ しています。Python のプログラムを実行すると、[`__name__`](https://qiita.com/ysgeso/items/a189461ae32a6531ff7f#__name__) など、いくつかの __グローバル変数が自動的に作られます__。実行結果の最初に表示される `{'__name__': '__main__'` などの部分はそのグローバル変数を表します。`__name__` については [前回の記事](https://qiita.com/ysgeso/items/a189461ae32a6531ff7f#__name__) で説明したが、他の自動的に作られるグローバル変数の意味については必要になった時点で説明します。

__JupyterLab で python のプログラムを実行__ した場合は、__JupyterLab が__ いくつかの __グローバル変数を使った処理を行います__、実行結果に表示される多くの名前は JupyterLab が作成したグローバル変数ですが、Python のプログラムを記述する際に、JupyterLab が利用したグローバル変数について __理解する必要はありません__ ので、それらの名前の意味については本記事では説明しません。

```python
print(globals())
```
実行結果（かなり長いので全体を見るためには右にスクロールする必要があります）
```
{'__name__': '__main__', '__doc__': 'Automatically created module for IPython interactive environment', '__package__': None, '__loader__': None, '__spec__': None, '__builtin__': <module 'builtins' (built-in)>, '__builtins__': <module 'builtins' (built-in)>, '_ih': ['', 'a = { "fruits": "apple", "price": 1000 }\nprint(a["fruits"])\nprint(a["price"])\nprint(a)', 'print(a.fruits)', 'a["color"] = "red"\nprint(a["color"])\nprint(a)', 'for key in a:\n    print(key)', 'print(a.keys())', 'for key in a:\n    print(a[key])', 'print(a.values())', 'for value in a.values():\n    print(value)', 'print(a.values()[0])', 'values = list(a.values())\nprint(values)\nprint(values[0])', 'print(a.items())', 'for item in a.items():\n    print(item)', 'for item in a.items():\n    print("key:", item[0], ", value:", item[1])', '(x, y) = (1, 2)\nprint(x, y)', 'for item in a.items():\n    (key, value) = item\n    print("key:", key, ", value:", value)', 'for (key, value) in a.items():\n    print("key:", key, ", value:", value)', 'x, y = 1, 2\nprint(x, y)', 'for key, value in a.items():\n    print("key:", key, ", value:", value)', 'print(1, 2)\nprint((1, 2))', 'print(globals())'], '_oh': {}, '_dh': [WindowsPath('c:/Users/ys/ai/marubatsu/020'), WindowsPath('c:/Users/ys/ai/marubatsu/020')], 'In': ['', 'a = { "fruits": "apple", "price": 1000 }\nprint(a["fruits"])\nprint(a["price"])\nprint(a)', 'print(a.fruits)', 'a["color"] = "red"\nprint(a["color"])\nprint(a)', 'for key in a:\n    print(key)', 'print(a.keys())', 'for key in a:\n    print(a[key])', 'print(a.values())', 'for value in a.values():\n    print(value)', 'print(a.values()[0])', 'values = list(a.values())\nprint(values)\nprint(values[0])', 'print(a.items())', 'for item in a.items():\n    print(item)', 'for item in a.items():\n    print("key:", item[0], ", value:", item[1])', '(x, y) = (1, 2)\nprint(x, y)', 'for item in a.items():\n    (key, value) = item\n    print("key:", key, ", value:", value)', 'for (key, value) in a.items():\n    print("key:", key, ", value:", value)', 'x, y = 1, 2\nprint(x, y)', 'for key, value in a.items():\n    print("key:", key, ", value:", value)', 'print(1, 2)\nprint((1, 2))', 'print(globals())'], 'Out': {}, 'get_ipython': <bound method InteractiveShell.get_ipython of <ipykernel.zmqshell.ZMQInteractiveShell object at 0x0000024DA52D9D90>>, 'exit': <IPython.core.autocall.ZMQExitAutocall object at 0x0000024DA52E6250>, 'quit': <IPython.core.autocall.ZMQExitAutocall object at 0x0000024DA52E6250>, 'open': <function open at 0x0000024DA32B0040>, '_': '', '__': '', '___': '', '__vsc_ipynb_file__': 'c:\\Users\\ys\\ai\\marubatsu\\020\\marubatsu.ipynb', '_i': 'print(1, 2)\nprint((1, 2))', '_ii': 'for key, value in a.items():\n    print("key:", key, ", value:", value)', '_iii': 'x, y = 1, 2\nprint(x, y)', '_i1': 'a = { "fruits": "apple", "price": 1000 }\nprint(a["fruits"])\nprint(a["price"])\nprint(a)', 'a': {'fruits': 'apple', 'price': 1000, 'color': 'red'}, '_i2': 'print(a.fruits)', '_i3': 'a["color"] = "red"\nprint(a["color"])\nprint(a)', '_i4': 'for key in a:\n    print(key)', 'key': 'color', '_i5': 'print(a.keys())', '_i6': 'for key in a:\n    print(a[key])', '_i7': 'print(a.values())', '_i8': 'for value in a.values():\n    print(value)', 'value': 'red', '_i9': 'print(a.values()[0])', '_i10': 'values = list(a.values())\nprint(values)\nprint(values[0])', 'values': ['apple', 1000, 'red'], '_i11': 'print(a.items())', '_i12': 'for item in a.items():\n    print(item)', 'item': ('color', 'red'), '_i13': 'for item in a.items():\n    print("key:", item[0], ", value:", item[1])', '_i14': '(x, y) = (1, 2)\nprint(x, y)', 'x': 1, 'y': 2, '_i15': 'for item in a.items():\n    (key, value) = item\n    print("key:", key, ", value:", value)', '_i16': 'for (key, value) in a.items():\n    print("key:", key, ", value:", value)', '_i17': 'x, y = 1, 2\nprint(x, y)', '_i18': 'for key, value in a.items():\n    print("key:", key, ", value:", value)', '_i19': 'print(1, 2)\nprint((1, 2))', '_i20': 'print(globals())'}
```

下記のプログラムは、1 ~ 4 行目で `a` という関数を定義して、6 行目で `a` を呼び出しています。関数 `a` では、__ローカル変数 `x` と `y` に値を代入__ した後で、組み込み関数 `locals()` を呼び出すことで、__ローカル名前空間__ を管理する __dict を表示__ しています。

実行結果から、__ローカル変数名をキー__ とし、その __キーの値__ として __ローカル変数に代入された値__ を持つ dict が表示されることを確認することが出来ます。

```python
1  def a():
2      x = 1
3      y = 2
4      print(locals())
5
6  a()
```

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

```python
def a():
    x = 1
    y = 2
    print(locals())

a()
```
</details>

実行結果
```
{'x': 1, 'y': 2}
```

:::note
下記のリンク先から参照できる Python 公式ドキュメントでは、「ほとんどの名前空間は、現状では Python の辞書（注: dict のこと）として実装されていますが、（略）、将来は変更されるかもしれません」と記述されているので、一部の名前空間は dict 以外の方法で表現されているかもしれません。また、今後のバージョンでは、辞書以外の方法で表現されるかもしれないようです。

本記事が利用している Python のバージョン 3.11 では、今回の記事で説明する名前空間は dict で実装されているようです。
:::

https://docs.python.org/ja/3/tutorial/classes.html#python-scopes-and-namespaces

## 用語の再確認

クラスとインスタンスの仕組みを理解するために必要な、dict の説明と、名前空間と dict の関係について説明したので、次は、クラスとインスタンスの仕組みについて説明します。


前回の記事で説明したように、クラスとインスタンスはいずれも __オブジェクトの一種__ なので、正式にはクラスオブジェクト、インスタンスオブジェクトのように表記しますが、表記が長くなるので __クラス__、__インスタンス__ のように __省略して表記される場合が多い__ ようです。本記事でもそのように省略して表記しますが、いずれもオブジェクトの一種であることを __忘れないで下さい__。

## クラスの仕組み

### クラスの定義の実行時に行われる処理

Python では __クラスの定義を実行__ すると、以下のような処理が行われます。

1. __クラスのローカル名前空間__ を表す __空の dict__ を管理する __オブジェクトが作成__ される
2. クラスの __ブロックの中__ に記述された __プログラムが実行__ される
3. __変数に値が代入__ されたり、__メソッドが定義__ された場合は、それらの __名前とオブジェクトの対応づけ__ が、__クラスのローカル名前空間に登録__ される
4. クラスの __ブロックの処理が終了__ した時点で、クラスを表すデータを管理する __オブジェクトが作成__ される。オブジェクトが管理するデータは、__ローカル名前空間__ を管理する __オブジェクトの id__ である
5. __クラス名__ と __同じ名前の変数__ に __クラスを表すデータが代入__ される

:::note
上記の手順は、下記のリンク先の Python の公式ドキュメントで説明されています。

例えば、上記の手順 4 で行われる処理は「クラスオブジェクトは、基本的にはクラス定義で作成された名前空間の内容をくるむラッパ (wrapper) です」のように説明されています。
:::

https://docs.python.org/ja/3.7/tutorial/classes.html#class-definition-syntax

__クラスの定義の実行__ と、__関数呼び出し__ は、「__ローカル名前空間が作られる__」、「__ブロックの中__ に記述された __プログラムが実行__ される」という 2 つの点では、__同様の処理__ が行われます。

__ブロックの処理__ を行う際の、クラスの定義と関数呼び出しの __相違点__ は以下の通りです

* __関数呼び出し__ の場合は、__仮引数__ によって __データを受け取る__ ことが出来る
* __クラスの定義__ の場合は、__仮引数のようなものは存在しない__
* 関数呼び出しの場合は、関数呼び出しが終了した時点で __関数のローカル名前空間は破棄__ される
* クラスの定義の実行の場合は、上記の手順 4 のように、クラスのローカル名前空間は __破棄されず__ に、__クラス__ が管理するデータによって __参照__ される

具体例として、前回の記事で定義した __`Marubatsu` クラス__ を例に説明します。ただし、下記の `Marubatsu` クラスの定義は、前回の記事の最後で追加した、イニシャライザである __`__init__` メソッドを削除__ しています。その理由については後で説明します。

```python
class Marubatsu:
    def initialize_board(self):
        self.board = [["."] * 3 for y in range(3)]

    def place_mark(self, x, y, mark):
        if self.board[x][y] == ".":
            self.board[x][y] = mark
        else:
            print("(", x, ",", y, ") のマスにはマークが配置済です")

    def display_board(self):   
        for y in range(3):
            for x in range(3):
                print(self.board[x][y], end="")
            print()      
```

上記の __`Marubatsu` クラスの定義を実行__ すると、以下のような処理が行われます。

1. `Marubatsu` クラスの __ローカル名前空間__ を表す __空の dict__ を管理する __オブジェクトが作成__ される（下図の ID が 351 のオブジェクト。なお、図はこの手順 1 ~ 5 を実行した後の状態です）
2. クラスの __ブロックの処理が開始__ される  
3. クラスのブロックの中に、__3 つのメソッドが定義__ されているので、それらのメソッドの定義を管理する __3 つのオブジェクトが作成__ される（下図の右の 3 つのオブジェクト）
4. `initialize_board`、`place_mark`、`display_board` という __3 つの名前__ と上記の __3 つのオブジェクト__ の __対応づけ__ が __クラスのローカル名前空間__ を表すオブジェクトに __登録__ される
5. クラスの __ブロックの処理が終了__ した時点で、__クラスが作成__ される（下図の ID が 852 のオブジェクト）。__クラスが管理するデータ__ は、__ローカル名前空間__ を管理する __オブジェクトの id__ である
6. __クラスを表すデータ__ が __`Marubatsu`__ という名前の変数に __代入__ される（下図のグローバル名前空間への登録）

下図は、__`Marubatsu` クラスの定義を実行__ した後の様子を図示したものです。ただし、図ではクラスの定義のブロックの中に記述したプログラムとは別に __自動的に作成__ される __特殊属性に関するデータは省略__ しています。真ん中の ID が 351 のオブジェクトが、__名前空間__ を表す dict を __管理するオブジェクト__ です。

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3524802/11e96483-ef70-8765-3853-72f2df30a133.png" width="800">

:::note warn
クラスの定義と、関数の定義は、どちらも __〇〇の定義__ のように記述されるので、同じような処理を行っていると __勘違いする__ 人がいるかもしれません。

実際には、__クラスの定義の実行__ で行われる処理に似ているのは、__関数呼び出し__ で行われる処理です。__関数の定義の実行__ で行われる処理とは __似ていない__ 点に注意して下さい。

例えば、__関数の定義を実行__ した際に、__関数の定義のブロックの中__ に記述されている処理は __実行されません__ が、__クラスの定義を実行__ した場合は、__クラスの定義のブロックの中__ に記述されている処理は __実行されます__。

もちろん、クラスの定義の実行と関数呼び出しは __異ななる処理__ なので、__似ていない部分__ も __数多く__ あります。例えば、__クラスの定義__ は基本的には __一度だけ実行されます__ が、__関数呼び出し__ は必要に応じて __何度でも実行される__ という点が異なります。
:::


### クラスの定義の実行で行われる処理の意味

クラスの定義の実行で行われる処理をまとめると以下のようになります。

1. __クラスのローカル名前空間を作成__ する
2. クラスのブロックの処理を実行し、そこで __定義された名前__ をクラスのローカル名前空間に __登録__ する
3. __クラスのローカル名前空間__ を管理する __クラスを作成__ する

前回の記事で、オブジェクトが __管理するデータ__ のこと __属性__（データが関数の場合は __メソッド__）と呼び、属性は「オブジェクト.属性名」と記述するという説明を行いました。これは、「属性名」や「メソッド名」という __名前__ から、名前に代入された値を管理する __オブジェクトを対応づける__ 処理が __必要になる__ ということを表します。実際に、Python のオブジェクトは、__自身が管理する名前__ と __データ__ を __対応づける名前空間__ をそれぞれ __個別に持っています__。

:::note warn
クラスの定義で行われる処理は、__クラスを作成__ し、そのクラスが __管理__ する属性やメソッドなどの __名前__ から、名前に代入されたデータを管理するオブジェクトを __対応づける__ ための __名前空間を作成__ するという処理です。

クラスのローカル名前空間は、関数のローカル名前空間と __同様の手順__ で作成されます。ただし、__関数のローカル名前空間__ が関数呼び出しが終了した時点で __破棄される__ のに対して、__クラスのローカル名前空間__ は __クラスの名前空間__ として __引き続き利用される__ ため、クラスの定義の実行が終了しても __破棄されません__。
:::



### クラスの属性名やメソッド名の名前解決

クラスの定義の実行によって作成された、`Marubatsu` などの __クラス__ に対して __`.名前` を記述__ した場合の __名前解決__ は以下の手順で行われます。手順の（）の中は、`Marubatsu.initialize_board()` を実行した場合の名前解決と下図の対応を表します。

1. __クラスが参照__ する、__名前空間__ を表す dict を __管理するオブジェクトを探す__（下図の②）
2. dict の __キー__ の中から __名前を探す__
3. __名前が見つかった__ 場合は、その __キーの値__ を管理するオブジェクトに __名前を対応づける__（下図の③）
4. 名前が __見つからなかった__ 場合は、__Attribute Error__ というエラーが発生する

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3524802/c0519d66-04e3-c2a2-1e37-d0026351b1ef.png" width="800">

以前に説明した名前解決の手順と __異なり__、名前が見つからなかった場合に __グローバル名前空間__ や __ビルトイン名前空間__ を __探しに行く__ ということは __しない__ 点に __注意して下さい__。

名前が半角の `.` で区切られている場合は、__先頭の名前__ から __順番に名前解決__ が行われます。例えば、`Marubatsu.initialize_board` のような記述を行うと、上図の ① で __グローバル名前空間__ を使って __`Marubatsu` の名前解決__ を行った後で、②、③ の順で __クラスの名前空間__ を使って __`initialize_board` の名前解決__ が行われ、ID が 533 の `initialize_board` の定義を管理するオブジェクトに対応づけられます。

:::note
厳密には、上記の手順 4 は正確ではなく、名前が見つからなかった場合に別の方法で名前解決を行う場合があります。具体的には、__いくつかの特殊属性__ は、名前空間を表す dict に __名前が登録されません__。また、まだ説明していないクラスの継承や、`__getattr__` などの特殊メソッドが定義されている場合などでは、別の方法で名前解決が行われます。

ただし、特殊属性以外の場合は、これまでに紹介してきたクラスの定義では発生しないので、基本的には、一部の特殊属性の名前を除いて、__上記の手順で名前解決が行われる__ と考えて __問題はありません__。一部の特殊属性の名前に関しては、何らかの特殊な方法で名前解決が行われていると思ってください。

別の方法で名前解決が行われる場合については、必要になった時点で紹介します。
:::

### 名前解決でどの名前空間が使われるかの見分け方

名前解決を行う際に、以前の記事で説明した __ビルトイン名前空間__、__グローバル名前空間__、__ローカル名前空間__ が使われるのか、今回の記事で説明した __オブジェクトの名前空間__ が使われるかがわかりづらいと思っている方が多いかもしれませんが、実際には __簡単に見分ける__ ことが出来ます。

:::note warn
名前解決を行う名前空間は以下の方法で見分けることができる。

* `board` や、`Marubatsu` のように、半角の `.` で始まらない、__先頭に記述された名前__ は、以前の記事で説明した __ビルトイン名前空間__、__グローバル名前空間__、__ローカル名前空間__ を使って名前解決が行われる
* __半角の `.` の後__ に記述された __名前__ は、__`.` の前__ に記述された __オブジェクトが管理する名前空間__ を使って名前解決が行われる

__`.` の後__ に記述された __名前__ の名前解決が行われる際に、ビルトイン名前空間、グローバル名前空間、ローカル名前空間は __使われない__ 点に注意して下さい。
:::

### クラスの名前空間の確認

__クラス__ の __名前空間__ を管理する dict は、クラスの __特殊属性 `__dict__`__ に自動的に __代入__ されます。従って、下記のプログラムのように、__`print(Marubatsu.__dict__)`__ を記述することで、`Marubatsu` のクラスの __名前空間を表示__ することが出来ます。

```python
print(Marubatsu.__dict__)
```
実行結果
```
{'__module__': '__main__', 'initialize_board': <function Marubatsu.initialize_board at 0x0000024DA5FCC0E0>, 'place_mark': <function Marubatsu.place_mark at 0x0000024DA5FCC900>, 'display_board': <function Marubatsu.display_board at 0x0000024DA5FCD120>, '__dict__': <attribute '__dict__' of 'Marubatsu' objects>, '__weakref__': <attribute '__weakref__' of 'Marubatsu' objects>, '__doc__': None}
```

__クラス__ は __作成された時点__ で、__`__dict__` 以外__ にも __いくつかの特殊属性__ が自動的に __作成__ されます。そのため、`Marubatsu.__dict__` には __多くの名前が登録__ されています。dict を __`print` で表示__ すると __横に並べて表示__ が行われるため、上記の実行結果のように __dict の全体像が分かりづらく__ なります。そこで、dict の __値をキーごとに改行して表示__ することにします。

dict の __値をキーごとに改行__ して表示するには、__pprint__[^5] というモジュールの中で定義されている __`pprint`__[^6] という名前の関数を利用するのが便利です。`pprint` は `print` と同様に、実引数の値を表示する処理を行う関数ですが、その際に __表示が見やすくなるように整形__ された表示を行ってくれます。

「[その 17](https://qiita.com/ysgeso/items/4d55e73d6571fe85aa49#%E6%A8%99%E6%BA%96%E3%83%A9%E3%82%A4%E3%83%96%E3%83%A9%E3%83%AA%E3%81%AE%E3%82%A4%E3%83%B3%E3%83%9D%E3%83%BC%E3%83%88)」の記事でも説明しましたが、python には、あらかじめいくつかのモジュールが用意されており、そのようなモジュールの事を __標準ライブラリ__（または __組み込みモジュール__）と呼びます。標準ライブラリは __インストールしなくても利用__ することが出来、pprint モジュールは標準ライブラリです。

python の標準ライブラリの詳細については、下記のリンク先を参照して下さい。

https://docs.python.org/ja/3/library/index.html

また、pprint モジュールの詳細については、下記のリンク先を参照して下さい。

https://docs.python.org/ja/3/library/pprint.html

下記のプログラムは 1 行目で、`pprint` モジュールの中の __`pprint` 関数をインポート__ し、3 行目で `pprint` を使って __`Marubatsu` の `__dict__` 属性を表示__ しています。`print` で表示した場合と異なり、__値がキーごとに改行__ されて表示が行われます。

```python 
from pprint import pprint

pprint(Marubatsu.__dict__)
```
実行結果
```
mappingproxy({'__dict__': <attribute '__dict__' of 'Marubatsu' objects>,
              '__doc__': None,
              '__module__': '__main__',
              '__weakref__': <attribute '__weakref__' of 'Marubatsu' objects>,
              'display_board': <function Marubatsu.display_board at 0x0000024DA5FCD120>,
              'initialize_board': <function Marubatsu.initialize_board at 0x0000024DA5FCC0E0>,
              'place_mark': <function Marubatsu.place_mark at 0x0000024DA5FCC900>})
```

実行結果に表示される __`mappingproxy`__ は、__値を変更できない__ 点以外は、__dict と同じ性質__ を持つデータ型で、`pprint` で表示すると、その内容は __`()` の中__ に __dict と同じリテラルで表示__ されます。

上記から、`__dict__` 属性に代入された dict には、__7 つのキーに対応する値が格納__ されていることがわかります。そのうちの __下の 3 つ__ が `Marubatsu` __クラスで定義__ された __メソッドの名前__ とその値を管理する __関数オブジェクト__ です。また、一番上に `__dict__` 属性とその値が格納されていることがわかります。

`__dict__` 属性は、クラスの属性やメソッドに対する処理を行うと __自動的にその値が更新されます__。クラスの `__dict__` 属性のデータ型は、値を変更することができない `mappingproxy` なので、クラスのメソッドの値を `__dict__` 属性を使って変更することはできません。

:::note
残りの 3 つの特殊属性の意味は以下の通りです。

* `__doc__`： __docstring__ に記述した __文字列が代入__ される特殊属性です。上記の `Marubatsu` の定義では、docstring が __記述されていない__ ので __None が代入__ されています
* `__module__`： __クラスの定義が実行__ された __モジュールの名前__ が代入される特殊属性です。上記の場合は、__メインモジュール__ で実行したので __`__main__` が代入__ されています
* `__weakref__`： この特殊属性は今回の記事の内容と関係がないので説明は省略します
:::

:::note
先程のノートで説明したように、全ての特殊属性の名前が `__dict__` に登録されるわけではありません。例えば、`__dict__` に登録されない特殊属性の一つに `__class__` 属性があります。この特殊属性の意味は必要になった時点で説明しますが、下記のプログラムのようにその値を表示することが出来ます。

```python
print(Marubatsu.__class__)
```
実行結果
```
<class 'type'>
```
:::

### クラスのメソッド

[前回の記事で説明](https://qiita.com/ysgeso/items/a189461ae32a6531ff7f#%E3%82%AF%E3%83%A9%E3%82%B9%E3%81%AE%E3%83%A1%E3%82%BD%E3%83%83%E3%83%89%E3%81%AE%E5%AE%9A%E7%BE%A9) したように、クラスの __メソッド__ を、__インスタンスから呼び出した場合__ は、メソッドの __仮引数 `self`__ に、メソッドを呼び出した __インスタンスが代入__ されます。

一方、クラスのメソッドを __クラスから呼び出した場合__ は、__通常の関数と同じ処理__ が行われるため、メソッドの __仮引数 `self`__ に、メソッドを呼び出したクラスが代入されることは __ありません__。

下記のプログラムは、`initialize_board` メソッドを、__クラス__ から __実引数を記述せず__ に呼び出していますが、実行結果からわかるように、__仮引数 `self` に対応する実引数が記述されていない__ という意味の __エラーが発生__ します。

```python
Marubatsu.initialize_board()
```
実行結果
```
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
c:\Users\ys\ai\marubatsu\020\marubatsu.ipynb セル 25 line 1
----> 1 Marubatsu.initialize_board()

TypeError: Marubatsu.initialize_board() missing 1 required positional argument: 'self'
```

`initialize_board` メソッドを __クラスから呼び出す__ 場合は __通常の関数と同様__ に、__仮引数と同じ数__ だけ __実引数を記述する必要__ があります。従って、__実引数にインスタンスを記述__ して `initialize_board` を呼び出すことで、__インスタンスから__ `initialize_board` __メソッドを呼び出た場合__ と __同じ処理__ が行われます。

意味が分かりづらいと思いますので、具体例を挙げて説明します。下記のプログラムの 2 行目と 3 行目は、__同一の処理__ を行うプログラムです。ただし、2 行目のプログラムは、メソッドを __クラスから呼び出した場合__ の処理を __説明するため__ に記述したものです。実際には __インスタンスに対する処理__ を行う __メソッドを呼び出す際__ に、__2 行目のような記述__ を行うことは __通常はありません__。

```python
mb = Marubatsu()
Marubatsu.initialize_board(mb) # この行と次の行は全く同じ処理を行う文である
mb.initialize_board()
```

:::note
参考までに、このことは、下記のリンク先の python の本家のドキュメントに「 `x.f()` という呼び出しは、`MyClass.f(x)` と厳密に等価なものです」のように説明されています。
:::

https://docs.python.org/ja/3/tutorial/classes.html#method-objects

:::note
__クラスのメソッド__ が __通常の関数と同じ性質を持つ__ ことは、先ほどの `pprint(Marubatsu.__dict__)` の実行結果の __`display_board` のキーの値__ が、__関数オブジェクトを表す `<function` で始まる__ ことから確認できます。後で説明しますが、__インスタンスのメソッド__ を `print` で __表示__ すると、上記とは __異なる表示__ が行われます。
:::

## インスタンスの仕組み

### インスタンスの作成と名前空間

前回の記事では、__インスタンス__ は、__クラス__ という __ひな形から作成__ される __オブジェクト__ であるという説明をしました。また、クラスからインスタンスを作成する際に、`Marubatsu()` のように、__関数呼び出しと同様の記述__ を行うという説明をしました。

:::note alert
そのため、インスタンスを作成する際に、関数呼び出しと同様の方法で、インスタンスのひな形であるクラスの定義のブロックの内容が実行された結果、クラスと同じ属性とメソッドを持つインスタンスが作成されると __勘違いしている人がいるかもしれません__。

（上記の説明は __間違った説明__ なので、背景が赤色のノートに記述しています）
:::

:::note warn
クラスと、インスタンスは、__全く異なる方法で作成__ されます。
:::

実際には、__インスタンスを作成__ する際に、以下のような処理が行われます。手順の（）の中は、`mb = Marubatsu()` を実行した場合の処理と下図の対応を表します。

1. __インスタンスの名前空間__ を表す __空の dict__ を管理する __オブジェクトが作成__ される（下図の ID が 669 のオブジェクト）
2. __インスタンスが作成__ される（下図の ID が 193 のオブジェクト）。__インスタンスが管理するデータ__ は、__インスタンスの名前空間__ を管理する __オブジェクトの id__ と、__クラスの id__ である
3. クラスの定義に __`__init__` メソッドが定義__ されている場合は、インスタンスの作成時に記述された実引数が仮引数に代入されて `__init__` メソッドが呼び出される。 

下図は、__`mb = Marubatsu()`__ を実行した __直後の状況__ を図示したものです。先ほどの `Marubatsu` クラスの定義には、`__init__` メソッドが定義されていないので、上記の手順 3 の処理は行われません。

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3524802/0c72c62c-7278-9ee3-3dbb-7c00d384969d.png" width="700">

図のように、__インスタンス__ はひな形となった __クラス__ と、__インスタンスの名前空間__ を管理する __オブジェクトを参照__ します。また、__`__init__` メソッドが定義されていない__ クラスから作成された __インスタンスの名前空間__ に __名前は__ 一つも __登録されません__。

先程、`Marubatsu` クラスからイニシャライザである `__init__` メソッドを削除した理由は、__インスタンスの作成時の処理の説明__ をする際に、インスタンスの __名前空間__ に __名前が登録されていない__ ということを説明したかったからです。

`Marubatsu` クラスの __`__init__` メソッド__ を前回の記事の最後で記述したように __定義__ すると、__インスタンスを作成した際__ に、インスタンスの __`board` 属性__ に初期化されたゲーム盤が __代入__ されてしまうため、インスタンスの __名前空間__ に `board` という名前が __登録__ されてしまいます。

このように、__`__init__` メソッド__ は、一般的にインスタンスを __作成した際__ に、インスタンスが __管理するデータ__ （属性）の __初期設定__ を行う処理を記述します。

### インスタンスの `__dict__` 属性

__インスタンス__ にも、クラスと同様に __インスタンスの名前空間が代入__ される __`__dict__` という特殊属性__ があります。下記のプログラムは、1 行目で `mb` に新しく作成した Marubatsu クラスのインスタンスを代入し、2 行目でその `__dict__` 属性を表示しています。__空の dict__ を表す __`{}` が表示__ されることから __インスタンスの名前空間__ に __名前が登録されていない__ ことがわかります。

```python
mb = Marubatsu()
print(mb.__dict__)
```
実行結果
```
{}
```

:::note warn
インスタンスを作成する際に、__クラスの定義が実行__ されることは __ありません__。また、`__init__` メソッドが定義されていないクラスから __作られた直後のインスタンス__ は、属性もメソッドも __持ちません__。

クラスの定義は、関数の定義と同様に、特別な場合を除いて __一度だけ実行__ されるものです。インスタンスの作成時に __関数呼び出しと同じ記述__ を行うのは、クラスの __`__init__` メソッドを呼び出す__ という処理が行われるためであり、クラスの定義が実行されるわけでは __ありません__。
:::

:::note
理由は不明ですが、クラスの `__dict__` 属性とインスタンスの `__dict__` 属性は若干性質が異なるようです。例えば、クラスの `__dict__` 属性には `__dict__` などの特殊属性の一部が登録されますが、インスタンスの `__dict__` 属性には特殊属性は一つも登録されません。他にもいくつかの性質が異なりますが、プログラムを記述する際に、`__dict__` 属性を利用することはほとんどないので、その違いを __気にする必要はない__ でしょう。
:::

### インスタンスの名前解決

__インスタンスの名前空間__ に __名前__ が一つも __登録されていない__ にも関わらず、__インスタンスからクラスで定義されたメソッドを呼び出すことが出来る__ ことを不思議に思う人が多いかもしれません。

__インスタンス__ に対して __`.名前` を記述__ した場合、以下の手順で名前解決が行われます。手順の（）の中は、`mb.initialize_board()` を実行した場合の名前解決と下図の対応を表します。

1. __インスタンスが参照__ する、__インスタンスの名前空間__ を管理する __オブジェクトを探す__（下図の②）
    1. 名前空間を表す dict の __キーの中__ から __名前を探す__
    2. __名前が見つかった場合__ は、その __キーの値__ を管理するオブジェクトに __名前を対応__ づける 
2. 名前が __見つからなかった場合__ は、__インスタンスが参照__ する、__クラスを探す__（下図の③）
3. __クラスが参照__ する __クラスの名前空間__ を管理する __オブジェクトを探す__（下図の④）  
    1. 名前空間を表す dict の __キーの中__ から __名前を探す__
    2. __名前が見つかった場合__ （下図の⑤）は、__下記の__ 名前が見つかった場合の __手順を行う__
    3. 名前が __見つからなかった場合__ は、__Attribute Error__ という __エラーが発生__ する

__手順 3-2__ で __名前が見つかった場合__ は、下記の処理を行います

1. キーの値を管理するオブジェクトが __関数オブジェクトでない__ 場合は、名前を __そのオブジェクトに対応づける__（この場合の処理の実例については次回の記事で説明します）
2. __関数オブジェクトの場合__ は、__メソッドオブジェクトを作成__ する（下図の⑥の ID が 193 のオブジェクト）
    1. __メソッドオブジェクトが管理するデータ__ は、__関数オブジェクトの id__ と、__インスタンスの id__ である
    2. 名前を作成した __メソッドオブジェクトに対応づける__

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3524802/3c23037e-138d-24b0-fc0a-bc04c8cb0fee.png" width="800">

上記の手順は一見すると複雑そうに見えるかもしれませんが、複雑なのは `.名前` に __メソッドの名前を記述__ した場合の __メソッドオブジェクトに関する処理だけ__ で、__それ以外の手順__ は、通常の __ローカル名前空間__、__グローバル名前空間__、__ビルトイン名前空間__ で行われる __名前解決の処理__ と __大きな違いはありません__。

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

1. __インスタンスの名前空間__ から __名前を探す__
2. 見つからなければ、__クラスの名前空間__ から __名前を探す__
3. それでも見つからなければ __エラーが発生__ する

__クラスの名前空間__ で __名前が見つかった場合__ は以下の処理を行います。

1. __属性名__ の場合は、その __属性の値を管理__ するオブジェクトに __対応づける__
2. __メソッド名__ の場合は、__メソッドオブジェクトを作成__ し、そのメソッドオブジェクトに __対応づける__
:::

__名前を見つけるまでの手順__ は、（__ビルドイン名前空間__ に相当する名前空間が __存在しない__ ことを除けば）過去の記事で説明した、__ローカル名前空間__ と __グローバル名前空間__ で行われる __名前解決__ と __同様の手順__ で処理が行われます。

それぞれの名前空間は以下の表のように対応づけることができます。

| 通常の名前空間 | オブジェクトの名前空間 |
|:--|:--|
| グローバル名前空間  | クラスの名前空間  |
| ローカル名前空間  | インスタンスの名前空間  |

### メソッドオブジェクトが作られる理由

__メソッドの名前__ が __クラスの名前空間__ で見つかった際に __メソッドオブジェクトが作成__ される __理由__ について説明します。先程の、__クラスからメソッドを呼び出した場合__ に __通常の関数呼び出しと同じ処理__ が行われるという説明を思い出して下さい。

__通常の関数__ は、呼び出された際に __最初の仮引数__ に関数を呼び出した __インスタンスを代入__ するという機能を __持ちません__。従って、インスタンスの __メソッドの名前__ を __クラスのメソッド__ に __そのまま対応づけてしまう__ と、インスタンスからメソッドを呼び出した際に、__仮引数 self__ にメソッドを呼び出した __インスタンスが代入されない__ ことになってしまいます。

__メソッドオブジェクト__ は関数と同じように __呼び出すことが出来るオブジェクト__ で、呼び出した際に、__最初の仮引数__ `self` にメソッドを呼び出した __インスタンスを代入__ する機能をもちます。

:::note warn
__メソッドオブジェクトに関する処理__ は、インスタンスからメソッドを呼び出した際に、__仮引数 self__ にメソッドを呼び出した __インスタンスが代入されるようにする__ ために行われるものです。
:::

### メソッドオブジェクトの確認

__インスタンス__ に __メソッドの名前__ を記述した際に、__メソッドオブジェクトに対応づけられる__ ことは、__`print`__ を使って、下記のプログラムの 3 行目のように確認することが出来ます。

__比較できるよう__ に、2 行目では __クラスのメソッドを表示__ しています。2 行目では __関数オブジェクトを表す `<function`__ で始まる表示が行われますが、3 行目では __異なる表示__ が行われます。

```python
mb = Marubatsu()
print(Marubatsu.initialize_board)
print(mb.initialize_board)
```
実行結果
```
<function Marubatsu.initialize_board at 0x0000024DA5FCC0E0>
<bound method Marubatsu.initialize_board of <__main__.Marubatsu object at 0x0000024DA52E69D0>>
```

:::note
3 行目の `print` で表示される内容の意味は、以下のようになります。なお、at の後ろに表示される数字は、オブジェクトの id です。

メインモジュール（\_\_main\_\_）で定義された `Marubatsu` クラスの（of） `initialize_board` に紐づけられた（bound）メソッド（method）オブジェクト。
:::

### インスタンスが管理するデータが独立したデータである理由

:::note warn
__`インスタンス.名前`__ を記述した場合は、その __名前__ は、その __インスタンスの名前空間__ を使って __名前解決__ が行われます。インスタンスの名前空間は、__インスタンスごとに作られる__ ので、__異なる__ インスタンスであれば、__異なる名前空間__ で __名前解決__ が行われます。

これが、__異なるインスタンス__ が管理するデータが __独立したデータ__ である理由です。
:::

具体例を、図を使って説明します。

```python
mb1 = Marubatsu()
mb2 = Marubatsu()
mb1.a = 1
mb2.a = 2
print(mb1.a)
print(mb2.a)
```
実行結果
```
1
2
```

下図は、上記のプログラムを実行した場合の状況を図示したものです。図からわかるように、`mb1` と `mb2` に代入されたインスタンスは、それぞれ ID が 669 と 014 のオブジェクトが管理する __異なる名前空間__ を __参照__ します。従って、`mb1` と `mb2` の `a` という属性の __名前解決__ を行う際に __異なる名前空間が使われる__ ため、それぞれのインスタンスが管理する `a` という __同じ名前の属性__ のデータは、図の ID が 234 と 345 のオブジェクトのように、__独立したデータ__ になります。

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3524802/05aa7918-f26b-1bc6-4500-d156da1e2984.png" width="800">

### メソッドをクラスで定義する理由

__メソッド__ はインスタンスが __管理するデータに対する処理__ を行うものです。それぞれのインスタンスが管理する __データが異なっていても__、一般的にインスタンスが管理する __データに対する処理__ は __共通__ します。

例えば、`Marubatsu` クラスから __複数のインスタンスを作成__ し、それぞれのインスタンスで __ゲーム盤にマークを配置__ していった場合、それぞれのインスタンスが管理する __`board` 属性の値は異なった値__ になります。しかし、それぞれのインスタンスの `board` 属性がどのような値を取ったとしても、`initialize_board`、`place_mark`、`display_board` メソッドは __同じ処理を行います__。

つまり、__同じクラスから作成__ されたインスタンスの __メソッドが行う処理__ は、__すべてのインスタンスで共通__ するということです。

:::note warn
メソッドをインスタンスではなく、クラスで定義するのは、__同じクラスから作成__ されたインスタンスが、__共通のメソッドを持つ__ ようにするためです。

__インスタンス__ が管理する __データは独立__ しているが、インスタンスが管理する __データを処理するメソッド__ はすべてのインスタンスで __共通する__ という性質は、今回の記事で説明した __インスタンスの名前解決の仕組み__ によって実現されています。
:::

:::note
このことは、__関数をグローバル名前空間で定義__ し、関数のブロックの中の __ローカル名前空間__ で、__グローバル空間で定義された関数を利用__ するという点に似ています。

__グローバル名前空間__ が __クラスの名前空間__、__ローカル名前空間__ が __インスタンスの名前空間__ に __対応する__ ことを思い出してください。
:::

# 次回の記事について

長くなりましたので、今回の記事はここまでにします。次回の記事では、今回の記事で説明しなかった、クラスの属性の使い方などについて説明します。

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

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

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

今回の記事では、`marubatsu.py` の変更はありません。

# 次回の記事

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

[^1]:例えば、`([1, 2], 3)` のように、tuple の要素の中にミュータブルな list が存在する場合は、このデータはハッシュ可能ではありません
[^2]:追加した順番でキーが取り出されるようになったのは、Python の バージョン 3.7 からです
[^3]:subscript は、$ x_1 $ の 1 のような、数学の変数の右下に小さく表示する __下付き文字__ のことで、__同じ名前__ の変数で __複数の数値を区別して扱う__ 際に使用します。python では、list や dict の右に記述する __`[]`__ の事を表します
[^4]:tuple について忘れた方は「[その 11](https://qiita.com/ysgeso/items/cc9e67c16b0814c8bc2e#tuple-%E3%81%AE%E6%80%A7%E8%B3%AA)」の記事を参照して下さい
[^5]:`pprint` の先頭の p は、「きれいな」という意味を表す pretty の頭文字です
[^6]:紛らわしいですが、`pprint` という __名前のモジュール__ の中に、__同じ名前__ の `pprint` という __関数が定義__ されています。`import pprint` でモジュールをインポートした場合は、この関数は `pprint.pprint` のように記述します
[^7]:正確には、書き込みができない以外は dict とほぼ同じ性質を持つ mappingproxy というデータ型です