Pythonで〇×ゲームのAIを一から作成する　その21　クラスの属性

# 目次と前回の記事

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

https://qiita.com/ysgeso/items/44884dcf168aab949508

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

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

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

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

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

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

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

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

## 前回までのおさらい

前回の記事では、dict とオブジェクトの名前空間について説明し、その後でクラスとインスタンスの仕組みについて説明しました。

# クラスの属性

これまでのプログラムでは、クラスの定義の中で、__メソッドの定義のみを記述__ してきましたが、クラスの定義の中で、__クラスのローカル変数に値を代入する__ という処理を __記述する__ ことで、__クラスの属性__ を定義することが出来ます。

[前回の記事](https://qiita.com/ysgeso/items/44884dcf168aab949508#%E3%82%AF%E3%83%A9%E3%82%B9%E3%81%AE%E5%AE%9A%E7%BE%A9%E3%81%AE%E5%AE%9F%E8%A1%8C%E3%81%A7%E8%A1%8C%E3%82%8F%E3%82%8C%E3%82%8B%E5%87%A6%E7%90%86%E3%81%AE%E6%84%8F%E5%91%B3) の「クラスの定義で行われる処理は、__クラスを作成__ し、そのクラスが __管理__ する属性やメソッドなどの __名前__ から、名前に代入されたデータを管理するオブジェクトを __対応づける__ ための __名前空間を作成__ するという処理です」という説明を思い出してください。

__クラスの定義を実行__ した際に、__クラスのローカル変数__ に __値を代入__ することで、__クラスのローカル名前空間__ にその __名前が登録__ されます。クラスの定義の実行が終了した時点で、__クラスのローカル名前空間__ は、__クラスの名前空間__ として __利用される__ ようになるので、クラスの __ローカル変数の名前__ が、そのままクラスの __属性の名前__ になります。

:::note warn
クラスの定義の中で、クラスの __ローカル変数に値を代入__ するという処理は、__クラスの属性__ に __値を代入__ する処理と __同じ意味__ を持ちます。
:::

具体的な記述例については少し後で説明します。

### 「クラスのローカル名前空間」と「クラスの名前空間」の違い

「クラスのローカル名前空間」と、「クラスの名前空間」は、名前が似ていますが、__異なる場面と方法__ で使われます。その違いが良くわからなくなっている人が多いかもしれないので、その違いについ補足します。

クラスのローカル名前空間は、__クラスの定義を実行する際に作成__ されるもので、クラスの定義の __ブロックの中__ に記述された __変数名__ や、__メソッド名__ などを __管理__ します。クラスのローカル名前空間は、__クラスの定義__ が __実行されてから終了するまでの間__ で利用されます。__クラスの定義の実行中__ に __名前解決__ を行う際に、クラスのローカル名前空間に名前が見つからなかった場合は、通常の名前解決で行われる手順と同様に、__グローバル名前空間__、__組み込み名前空間__ の順で名前を探します。

クラスの名前空間は、クラスの定義の実行が終了した時点で、__クラスのローカル名前空間__ が __そのまま引き継がれて利用__ されます。クラスの名前空間は、クラスが管理するデータなので、__クラスの定義を実行した後__ は __いつでも利用__ することが出来ます。クラスの名前空間は、__`クラスの名前.属性名`__ のような記述が行われた際に利用されます。クラスの名前空間に名前が見つからなかった場合は、AttributeError という __エラーが発生__ します。グローバル名前空間やローカル名前空間を探しに行くことは __ありません__。

:::note
前回の記事のノートでも言及しましたが、まだ説明していないクラスの継承が行われている場合や、クラスの名前空間に登録されない特殊属性などで、クラスの名前空間に名前が見つからなかった場合でもエラーが発生しない場合がありますが、__基本的にはエラーが発生する__ と考えて良いでしょう。
:::

下記の表は上記の説明をまとめたものです。

| | クラスのローカル名前空間 | クラスの名前空間 |
|:--|:--|:--|
| __対象となる名前__  | クラスの定義のブロックの中の変数名やメソッド名  | `クラス名.` の後に記述された名前  |
| __スコープ__  | クラスの定義のブロックの中  |  クラスの定義を実行した後 |
| __名前が見つからなかった場合__  | グローバル名前空間、組み込み名前空間の順で名前を探す | エラーが発生する  |

## クラスの属性の使い道

__クラスの属性__ は、__全てのインスタンス__ で __共通するデータ__ を __扱う__ 場合に利用します。

:::note
逆に言うと、すべてのインスタンスで共通するデータが __存在しない__ 場合は、クラスの属性を利用する __必要はありません__。
:::

`Marubatsu` クラスを具体例に挙げて説明します。`Marubatsu` クラスでは、ゲーム盤に配置する __マーク__ を __文字列で表現__ しており、マークを表す文字列は、すべての `Marubatsu` クラスのインスタンスで __共通するデータ__ です。例えば、`Marubatsu` クラスのインスタンスによって、〇 を表す文字列が異なるようなことはありません[^1]。従って、__`Marubatsu` クラスの属性__ にマークを表す文字列を代入して利用することができます。

## クラスの属性を使う利点

クラスの属性を利用する利点がピンとこない人が多いかもしれません。そこで、今回の記事では `Marubatsu` クラスを具体例に挙げて、「__プログラムの修正が容易になる__」と、「__データの意味が分かりやすくなる__」の 2 つの利点について説明します。

:::note
実際には、`__init__` メソッド内で __インスタンスの属性__ にマークを表す文字列を代入して利用することでも、これから説明する利点を得ることが出来ますが、インスタンスの属性は本来は __インスタンスごとに異なる__ ようなデータを代入する __ためのもの__ なので、__すべてのインスタンスで共通する__ データは __クラスの属性を利用__ したほうがプログラムの __意味が分かりやすくなる__ でしょう。
:::

### プログラムの修正が容易になる

`Marubatsu` クラスの定義では、__最初は__ 空白のマスを表す文字列を __半角の空白文字__ で表現していましたが、__後から半角の `"."` に変更__ するという __修正__ を行いました。同様に、〇 や × のマークを表す文字列も __全角文字__ から __半角文字__ に __変更__ しました。

プログラムでこのような __変更__ を行うと、変更した内容に __関連するプログラム__ を __すべて修正__ しないと __バグが発生__ してしまいます。実際に [過去の記事](https://qiita.com/ysgeso/items/c5dbe62b329f7936181a#%E4%B8%8A%E8%A8%98%E3%81%AE%E3%83%97%E3%83%AD%E3%82%B0%E3%83%A9%E3%83%A0%E3%81%AE%E3%83%90%E3%82%B0) で、空白のマスを表す文字列を半角の空白文字から半角の `"."` に __変更__ した際に、__`place_mark`__ で行う処理を __修正し忘れた__ ため、__バグが発生__ しました。

このような問題は、データを __直接__ プログラムに __記述__ するの __ではなく__、__わかりやすい名前__ の __変数や属性__ にデータを __代入__ し、その __変数や属性__ をプログラムに __記述する__ という方法で __改善__ することが出来ます。

### データの意味が分かりやすくなる

プログラムにデータを直接記述するのではなく、分かりやすい名前の属性にデータを代入し、その名前をプログラムに記述することで、プログラムのデータの意味が分かりやすくなるという利点が得られます。

`Marubatsu` クラスの場合は、空白、〇、× のマスを表す __文字列__ を、__分かりやすい名前__ のクラスの __属性に代入__ し、__その属性を使って__ プログラムを __記述__ します。

## クラスの属性の記述方法

上記の利点の具体例を説明するために必要な知識として、クラスの属性の記述方法について説明します。


### クラスの属性への値の代入

一般的に、__クラスの定義の中__ で、__クラスの属性に値を代入__ する処理は、クラスの __メソッドの定義を記述する前に記述__ します（理由は後述します）。`Marubatsu` クラスの場合は、下記のように記述します。

```python
class Marubatsu:
    EMPTY = "."
    CIRCLE = "o"
    CROSS = "x"

    def __init__(self):
        # 以下略
```

__属性の名前__ は、空を表す `EMPTY`、〇 を表す `CIRCLE`、× を表す `CROSS` にしました。また、マスのマークを表す文字列は、〇×ゲームの __実行中に変化__ することは __ありません__。これらの属性の __名前を全て大文字__ にしたのは、python では、プログラムの __実行中に値が変化しない__ ような変数や属性の __名前__ を __大文字__ と __半角の `_`__（アンダースコア） __のみで記述__ するという __慣習がある__ からです。

:::note
プログラム言語によっては、一度値を代入すると、別の値を代入できなくなる __定数__ と呼ばれる仕組みを持つものがあります。残念ながら __Python__ には __定数__ の仕組みは __ありません__ ので、上記のように __名前を全て大文字__ にしても、__別の値を代入__ することが __できてしまいます__。ただし、名前を全て大文字にすることで、その名前の変数や属性の値を __変えるべきではない__ ことが __明確__ になります。__間違って__ 別の値を代入するという処理を __記述しにくくなる__ という利点が得られるので、この慣習は __積極的に利用したほうが良い__ でしょう。
:::

__クラスの定義の中__ では、__属性の名前__ は `self.属性名` __ではなく__、__変数名と同様__ に __名前だけを記述__ する点に __注意して下さい__。その理由についてはこの後で説明します。

上記のプログラムで、__`self.EMPTY = "."`__ のように記述すると、__`self`__ という __名前の変数__ が __定義されていない__ ので __エラーが発生__ します。__`self` を利用できる__ のは、__仮引数 `self` が記述__ されている __メソッドのブロックの中だけ__ です。 

:::note
__`self`__ は __特別__ な名前 __ではない__ ので、`self = 1` のような代入文を実行することで、`self` に任意のデータを代入することが出来ます。ただし、__`self`__ という __名前__ をメソッドを呼び出したインスタンスを代入する __以外の目的__ で利用すると __プログラムの意味が分かりにくくなる__ ので、__避けたほうが良い__ でしょう。
:::

### メソッドの定義のブロックでのクラスの属性の記述

次に、クラスの __メソッドの定義のブロックの中__ で、空白のマスを表す __`"."` の部分__ を、対応する __クラスの属性__ に __置き換える必要__ があります。

:::note
現時点ではクラスの定義の中に 〇 と × を表すデータは __記述されていない__ ので `CIRCLE` と `CROSS` 属性に関する __修正__ を行う必要はは __ありません__。
:::

__メソッドの定義の中__ に __クラスの属性を記述__ する場合は、__`Marubatsu.EMPTY`__ のように、__`クラスの名前.`__ を __属性の名前の前__ に __記述する必要__ があります。その理由についてはこの後で説明します。

下記が修正したプログラムです。変更箇所の確認は、下記の修正箇所をクリックして下さい。

```python
class Marubatsu:
    EMPTY = "."
    CIRCLE = "o"
    CROSS = "x"

    def __init__(self):
        self.initialize_board()

    def initialize_board(self):
        self.board = [[Marubatsu.EMPTY] * 3 for y in range(3)]

    def place_mark(self, x, y, mark):
        if self.board[x][y] == Marubatsu.EMPTY:
            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()      
```

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

```diff_python
class Marubatsu:
+   EMPTY = "."
+   CIRCLE = "o"
+   CROSS = "x"

    def __init__(self):
        self.initialize_board()

    def initialize_board(self):
-       self.board = [["."] * 3 for y in range(3)]
+       self.board = [[Marubatsu.EMPTY] * 3 for y in range(3)]

    def place_mark(self, x, y, mark):
-       if self.board[x][y] == ".":
+       if self.board[x][y] == Marubatsu.EMPTY:
            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()      
```
</details>

:::note warn
__クラスの属性__ は、__クラスの定義の中__ では以下のように記述する。

* メソッドの定義の __ブロックの外__ では、__属性名のみ__ を記述する
* メソッドの定義の __ブロックの中__ では、__`クラスの名前.属性名`__ を記述する
:::

### クラスの属性の記述に関する補足説明

メソッドの __定義の外__ でクラスの属性を記述する場合は、`EMPTY = "."` のように、__属性名だけ__ を記述すれば良いのに、メソッドの __定義の中__ では `Marubatsu.EMPTY` のように __クラスの名前を記述する__ 必要がある点を不思議に思う人が多いかもしれません。

この違いを理解するためには、__メソッドの定義の外__ に記述されたプログラムと、__メソッドの定義の中__ に記述されたプログラムの __違い__ について、正しく理解する必要があります。ただし、その違いを理解するために新しいことを覚える必要はありません。

メソッドは、__関数と同様の性質__ を持つので、メソッドの定義を実行した際には、関数の定義を実行した際と __同様の処理__ が行われます。従って、[過去の記事](https://qiita.com/ysgeso/items/95282cb1532602799052#%E9%96%A2%E6%95%B0%E3%81%AE%E5%AE%9A%E7%BE%A9) で説明したように、メソッドの定義を実行した際に、__メソッドの名前__ が __名前空間に登録__ されますが、その際にメソッドの定義の __ブロックの中__ に記述されたプログラムは __実行されません__。メソッドの定義のブロックの中に記述されたプログラムは、関数の定義のブロックと同様に、__メソッドを呼び出した際に実行__ されます。

一方、クラスの定義の中で、__メソッドの定義の外__ に記述されたプログラムは、クラスの定義を実行した際に、実際に __実行されます__。

:::note warn
__クラスの定義を実行__ した際に、以下のような処理が行われる。

* メソッドの定義の外に記述されたプログラムは __実行される__
* メソッドの定義の中に記述されたプログラムは __実行されない__
:::

__`Marubatsu` クラスの定義を実行__ した際に行われる処理は以下のようになります。

下記の 2 ~ 4 行目の 3 つの代入文は __メソッドの外__ に記述されているので __実行され__、3 つの変数の名前が `Marubatsu` クラスのローカル名前空間に登録されます。

6、7 行目のような、__メソッドの定義__ も __実行され__、`__init__` というメソッドの __名前__ が `Marubatsu` クラスの __ローカル名前空間に登録__ されますが、その際に 7 行目のような、__メソッドの中__ に記述されているプログラムは、`Marubatsu` クラスの定義を実行した際に __実行されません__。これは関数の定義を実行した際に、関数の定義のブロックの中のプログラムが実行されないのと同様です。

```python
1  class Marubatsu:
2      EMPTY = "."
3      CIRCLE = "o"
4      CROSS = "x"
5
6      def __init__(self):
7          self.initialize_board()
8
9      以下略
```
下図は、`Marubatsu` クラスの定義を実行した後で、`Marubatsu` クラスの定義の __ブロックの処理がすべて終了した時点__ の状況を表しています。図からわかるように、`Marubatsu` クラスの __ローカル名前空間__ に、__3 つの変数名__ と、__3 つのメソッドの名前__ が登録され、それぞれの名前に対して __オブジェクトが対応づけ__ られています。

:::note
上記で「3 つの変数」のように表記したのは、この時点では、`Marubatsu` クラスはまだ __作成されていない__ ので、`EMPTY` などは属性ではなく、__ローカル変数__ だからです。`EMPTY` などがクラスの属性になるのは、クラスが __作成された後__ です。
:::

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3524802/664ef367-e6ee-7bd9-8978-5382259a2179.png" width="600">

__クラスの定義を実行__ すると、__クラスの定義のブロック__ に記述されたプログラムが __実行__ され、__その後__ で __クラスが作成__ されます。__クラスの名前__ がグローバル __名前空間に登録__ されるのは、__クラスが作成された時__ なので、図からわかるように、この時点では __クラスの名前__ はグローバル名前空間に __登録されていません__。


先程説明したように、__クラスの定義を実行__ した際に、クラスのブロックの中で行われる名前解決は、__通常の名前解決の手順と同様__ に、「クラスのローカル名前空間」、「グローバル名前空間」、「ビルトイン名前空間」の順で行われます。図からわかるように、`Marubatsu` という名前はどの名前空間にも登録されていないので、`Marubatsu.EMPTY` のような記述を実行すると、`Marubatsu` という名前の解決を行うことが出来ないのでエラーが発生します。これが、メソッドの __定義の外__ でクラスの属性を記述する場合は、`EMPTY = "."` のように、__属性名だけ__ を記述する理由です。

下図は、`Marubatsu` クラスが __作成__ され、その名前が __グローバル名前空間に登録__ された直後の状況を表しています。先ほどの図の `Marubatsu` のローカル名前空間がそのまま `Marubatsu` の名前空間として __流用__ されています。`Marubatsu` という名前がグローバル名前空間に登録された結果、今後は __いつでも__ `Marubatsu.CROSS` のように記述することで、__`Marubatsu` クラスの名前空間を利用__ することが出来るようになります。

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3524802/3f686656-826a-cee2-a33c-4f9b43cbaecd.png" width="600">

メソッドの定義のブロックに記述されたプログラムは、__メソッドが呼び出された際に実行__ されますが、メソッドは以下のような順番で、インスタンスから __呼び出されます__。

1. クラスの定義を実行する
2. クラスからインスタンスを作成する
3. 作成したインスタンスからメソッドを呼び出す

上記の順番からわかるように、メソッドが呼び出された時点では、__クラスの定義__ は __実行済__ なので、その __クラスの名前__ は、__グローバル名前空間に登録__ されています。

また、メソッドは関数と同様の性質を持つので、メソッドが呼び出された際に、メソッドのブロックの中では、その __メソッドのローカル名前空間__ が作られます。

従って、メソッドのブロックの中で、`EMPTY` のような記述を行うと、メソッドのローカル名前空間、グローバル名前空間、組み込み名前空間の順で名前解決が行われますが、この名前はこの 3 つのどの名前空間にも登録されていないので、エラーが発生します。

一方、`Marubatsu` という名前は __グローバル名前空間に登録__ されており、`EMPTY` という名前は __`Marubatsu` クラスの 名前空間に登録__ されています。従って、メソッドのブロック内で `Marubatsu` クラスの `EMPTY` 属性は `Marubatsu.EMPTY` のように記述できます。

:::note
クラスの定義を実行した際に、クラスのローカル名前空間に `EMPTY` という名前が登録されるので、その名前空間を使えば `EMPTY` の名前解決を行うことが出来ると思う人がいるかもしれません。しかし、クラスのローカル名前空間と、メソッドのローカル名前空間は __異なる名前空間__ です。また、メソッドのブロックの中の名前解決で使われる名前空間は、メソッドのローカル名前空間、グローバル名前空間、組み込み名前空間の 3 種類で、クラスのローカル名前空間は __使われません__。
:::

前回の記事で説明するのを忘れましたが、`__init__` メソッドの定義のブロックの中から、`initialize_board` メソッドを呼び出す際に、`self.initialize_board()` のように記述する必要がある理由は、上記で説明した理由と同様です。

:::note
下記のプログラムのように、クラスのブロックの中で __グローバル変数__、__グローバル関数__、__組み込み関数__ に対する __処理を行う__ ことができます。

```python
a = [1]

def add(x, y):
    return x + y

class A:
    # クラスの定義のブロックの中で、グローバル変数 a の要素に値を代入する
    a[0] = 2
    # クラスの定義のブロックの中で、グローバル関数 add と組み込み関数 print を利用する
    print(add(1, 2))

# クラスの定義の実行により、グローバル変数 a の値が変化する
print(a)
```
実行結果
```
3
[2]
```

クラスのブロックの中で __グローバル関数__ や __組み込み関数__ を利用しても基本的には __問題は発生しません__ が、グローバル変数に対して上記の `a[0] = 2` のような処理を実行すると、クラスの定義の実行によってグローバル変数の値が変化する（__副作用が発生する__）ことになるので、特別な理由がない限り、そのような処理は記述しないほうが良いでしょう。これは、過去の記事で説明した [関数の副作用](https://qiita.com/ysgeso/items/b0732427b90a1ec27243#%E9%96%A2%E6%95%B0%E3%81%AE%E5%89%AF%E4%BD%9C%E7%94%A8) と同じなので、忘れた方は復習して下さい。
:::

### クラスの属性の代入を、メソッドの定義より前に記述する理由

__クラスの属性に値を代入__ する処理を、クラスの __メソッドの定義の前__ に記述する理由は、__プログラムをわかりやすくする__ ためです。具体例を挙げて説明します。

下記のプログラムは、クラスの属性 `X` に `1` を代入し、クラスの属性 `X` を表示する `printX` メソッドを持つ `A` というクラスを定義しています。

```python
class A:
    X = 1

    def printX():
        print(A.X)
```

上記のプログラムを、下記のプログラムのように、`X = 1` を __メソッドの定義の後__ に記述するように修正しても __全く同じクラス__ が作成されますが、下記のプログラムでは、`X = 1` がメソッドの定義の後に記述されているので、__上から順番__ にこのプログラムを __見た際__ に、3 行目の `print(A.X)` で行われる __処理の意味__ が、その後の 5 行目で記述された `X = 1` を __見るまで分かりません__。

一方、上記のプログラムの場合は、5 行目の `print(A.X)` より __前に__ `X = 1` が記述されているので、__上から順番__ にプログラムを見た際に、5 行目で行われる処理の __意味が理解しやすくなる__ という利点が得られます。

```python
class A:
    def printX():
        print(A.X)

    X = 1
```

### 動作の確認

先程修正した `Marubatsu` クラスが __正しく動作__ することを __確認__ します。

下記のプログラムでは、1 行目で `Marubatsu` クラスのインスタンスを作成し、2 行目で `place_mark` メソッドを呼び出して (0, 0) のマスに 〇 を配置し、3 行目で `mb.display_board()` を呼び出してゲーム盤を表示しています。実行結果から、プログラムが正しく動作することが確認できます。

```python
mb = Marubatsu()
mb.place_mark(0, 0, "o")
mb.display_board()
```
実行結果
```
o..
...
...
```

## クラスの属性の利点の具体例

### 利点その 1：マークを表すデータをわかりやすく記述できる

マークを表す文字列を、クラスの属性に代入することで、次のような、__マークを表すデータをわかりやすく記述できる__ という利点が得られます。

例えば、`Marubastu` クラスの事を __よく知らない人__ が __`mb.place_mark(0, 0, "o")`__ というプログラムを見たときに、__`"o"`__ が小文字のオーではなく、〇 というマークを表す文字列だとすぐに __理解する__ ことは __困難__ だと思いませんか？一方、下記のプログラムの 2 行目の __`Marubatsu.CIRCLE`__ のような記述であれば、少なくとも __〇（circle）に関する__ データを実引数に記述しているということを __推測する__ ことは容易でしょう。

実行結果から、正しいマスに 〇 のマークが配置されることが確認できます。

```python
mb = Marubatsu()
mb.place_mark(0, 0, Marubatsu.CIRCLE)
mb.display_board()
```

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

```diff_python
mb = Marubatsu()
- mb.place_mark(0, 0, "o")
+ mb.place_mark(0, 0, Marubatsu.CIRCLE)
mb.display_board()
```
</details>


実行結果
```
o..
...
...
```

同様に、`initialize_board` メソッドの中に記述された `[["."] * 3 for y in range(3)]` の __半角の `"."`__（ピリオド）が空白を表す文字列であることを __理解する__ ことは __困難__ ですが、`[[Marubatsu.EMPTY] * 3 for y in range(3)]` という記述であれば、`Marubatsu.EMPTY` が __空白（empty）__ を表すデータであることを __容易に推測__ できるでしょう。

また、クラスの属性を利用することで、〇 を表す文字列が何であるかを __覚える必要がなくなる__ という利点を得ることができます。もちろん、〇 を表す文字列が代入された __属性名を覚える必要__ はありますが、そのことは __覚えやすい属性名__ をつけたり、__docstring を記述する__ などの方法である程度は解決することが出来ます。

### 利点その 2：プログラムの変更が容易になる

__クラスの属性を利用__ するプログラムを記述することで、クラスの定義の中の、`EMPTY` などの __属性に代入する文字列を変更__ する __だけ__ で、__マスを表す文字列__ を __簡単に変更__ することが出来ます。例えば、__空白のマス__ を表す文字列を __半角の `"-"`__（マイナス） に変更したい場合は、下記のように __`EMPTY`__  に __代入する値を変更__ する __だけ__ で、__他の部分__ は一切 __変更する必要はありません__。

```python
class Marubatsu:
    EMPTY = "-"
    # 略
```

#### クラスの定義の実行後のクラスの属性への代入

__クラスの属性を変更__ または __追加__ するたびに、__クラスの定義を全て記述__ しなおして実行するのは __大変__ です。__既に定義__ された __クラスの属性__ の __変更__ または __追加__ は、下記のプログラムのように、__`クラス名.属性名`__ に __値を代入__ することで簡単に行うことが出来ます。実行結果から、1 行目の処理によって、空白のマスを表す文字列が `"-"` に変化することを確認できます。

```python
Marubatsu.EMPTY = "-"
mb = Marubatsu()
mb.display_board()
```
実行結果
```
---
---
---
```

### 利点その 3：数値データの意味が分かりやすくなる

利点その 1 で __マークを表す文字列__ をクラスの属性に代入したように、一般的にプログラムの中で何らかの __意味のあるデータ__ を __変数や属性に代入__ することで、__利点__ が得られる場合があります[^2]。特に、意味のある __数値データ__ を変数や属性に代入することで利点が得られる __場合が多い__ ので、具体例を挙げてそのことを説明します。

例えば、`Marubatsu` クラスの中では、__`3` という数値__ が __頻繁に記述__ されています。この数値は、__ゲーム盤の縦横のサイズ__ を表す数値です。現時点では記述したプログラムが短いので、プログラム内に記述された `3` が何を表す数値であるかがわからなくなることは無いかもしれませんが、__プログラムの量が増える__ と、__様々な数値__ がプログラムの中に __記述されるようになります__。

また、プログラムの中で __同じ数値が異なる意味__ で記述されることが良くあります。実際に `initialize_board` メソッド内の `for x in range(3)` の `3` はゲーム盤の __列__ の数を、`for y in range(3)` の `3` はゲーム盤の __行__ の数を表す数値で、__同じ `3`__ であってもその __意味は異なっています__。そのような場合に、後からゲーム盤の __列__ の数を変更するには、__数多くの__ `3` という数字の中から、列を表す `3` __だけを探し出して変更する__ という __大変な作業__ が必要になります。

このように、プログラムの中に記述した __`3` のような数値__ が __何を表すかを理解__ することが __困難__ になる場合が良くあります。そこで、〇×ゲームのゲーム盤のサイズを __クラス__ の __`BOARD_SIZE`__ という __属性に代入__ し、その __属性を使って__ プログラムを __修正__ することにします。先ほど説明したように、定義済の __クラス__ への __新しい属性の追加__ は、下記のプログラムのように __代入文を記述__ するだけで行えます。
```python
Marubatsu.BOARD_SIZE = 3
```

:::note
〇×ゲームのゲーム盤は __正方形__ なので、`BOARD_SIZE` という一つの属性を使うことにしましたが、__長方形__ のゲームの場合は、横のサイズを `BOARD_WIDTH`、縦のサイズを `BOARD_HEIGHT` で表すように、__2 つの属性__ が必要となります。
:::

#### クラスの定義の実行後のメソッドの修正

次に、__ゲーム盤のサイズを表す `3` が記述__ されている __メソッドを修正__ する必要がありますが、メソッドを修正するたびに __クラスを定義し直すのは大変__ です。__属性の場合と同様__ に、__メソッドの修正や追加__ も、__メソッドの処理を行う関数を定義__ してから、__クラスの属性__ に定義した __関数を代入する__ ことで行うことが出来ます。[過去の記事](https://qiita.com/ysgeso/items/cd3e1c0731b859bf5e2c#%E5%A4%89%E6%95%B0%E3%81%AB%E9%96%A2%E6%95%B0%E3%82%92%E7%9B%B4%E6%8E%A5%E4%BB%A3%E5%85%A5%E3%81%99%E3%82%8B%E4%BE%8B) の、__変数に関数を代入__ することで、その __変数が、代入した関数と同じ機能を持つ__ ようになるという説明を思い出してください。

下記は、`3` が記述されている `initialize_board` メソッドと、`display_board` メソッドを修正したプログラムです。このように修正することで、プログラムの記述が少し長くなるという欠点はありますが、それを上回る __プログラムの意味が分かりやすくなる__ という利点が得られます。

```python
def initialize_board(self):
    self.board = [[Marubatsu.EMPTY] * Marubatsu.BOARD_SIZE for y in range(Marubatsu.BOARD_SIZE)]

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

Marubatsu.initialize_board = initialize_board
Marubatsu.display_board = display_board
```

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

```diff_python
def initialize_board(self):
-   self.board = [[Marubatsu.EMPTY] * 3 for y in range(3)]
+   self.board = [[Marubatsu.EMPTY] * Marubatsu.BOARD_SIZE for y in range(Marubatsu.BOARD_SIZE)]

def display_board(self):
-   for y in range(3):
+   for y in range(Marubatsu.BOARD_SIZE):
-       for x in range(3):
+       for x in range(Marubatsu.BOARD_SIZE):
            print(self.board[x][y], end="")
        print()  
```
</details>


下記は、上記で修正した `Marubatsu` クラスが正しく動作しているかどうかを確認するプログラムです。実行結果から、正しく動作していることを確認することが出来ます。

```python
mb = Marubatsu()
mb.place_mark(0, 0, Marubatsu.CIRCLE)
mb.display_board()
```
実行結果
```
o--
---
---
```

#### プログラムの修正が容易になるという利点

〇×ゲームは 3x3 のゲーム盤で行うゲームなので、このような修正を行う必要はありませんが、マークを表す文字列を属性に代入した場合と同様に、__ゲーム盤のサイズを表す数値__ を __`BOARD_SIZE` 属性に代入__ することで、下記のプログラムのように、他のプログラムを __一切変更することなく__、ゲーム盤のサイズを __5x5 に 変更__ することが出来ます。

```python
Marubatsu.BOARD_SIZE = 5

mb = Marubatsu()
mb.display_board()
```
実行結果
```
-----
-----
-----
-----
-----
```

## クラスの属性の代入に関する注意点

クラスの定義を実行した後で、__クラスの属性やメソッド__ に対して __代入処理を行う__ ことで __属性やメソッド__ の __修正や追加__ を簡単に行うことが出来ますが、修正したクラスを __モジュールとして保存__ し、他のファイルから利用する際には、__修正した部分を反映__ した __クラスの定義__ をモジュールの中に __記述__ する必要があります。

記事の最後にリンクで示す github の __marubatsu_new.py__ には、その回の __記事の修正を反映したクラスの定義__ を記述します。

## クラスとインスタンスの属性の使い分け

:::note warn
クラスの属性を使うか、インスタンスの属性を使うかは、その __属性の値__ がすべてのインスタンスで __共通するかどうか__ によって以下のように __使い分ける__ 必要がある。

* 全てのインスタンスで __共通するデータ__ は、__クラスの属性__ に代入する
* 個々のインスタンスで __独立するデータ__ は、__インスタンスの属性__ に代入する
:::

例えば、インスタンスによって、__ゲーム盤のサイズを変更__ できるようにしたい場合は、ゲーム盤のサイズを表す __属性__ は、__インスタンスの属性__ にする必要があります。

### インスタンスの作成時の実引数を使ったインスタンスの属性の設定

インスタンスの __作成時__ に、__実引数を使って__ インスタンスの __属性の値を設定__ したい場合は、__`__init__` メソッドの仮引数__ を使います。例えば、`Marubatsu` クラスで、インスタンスの __作成時__ に __ゲーム盤のサイズを設定__ したい場合は下記のプログラムのように `Marubatsu` クラスの定義を修正します。なお、今回は、`Marubatsu` クラスの定義からの修正箇所がわかるようにするために、クラスを定義し直すことにします。

```python
class Marubatsu:
    EMPTY = "."
    CIRCLE = "o"
    CROSS = "x"

    def __init__(self, board_size=3):
        self.BOARD_SIZE = board_size
        self.initialize_board()

    def initialize_board(self):
        self.board = [[Marubatsu.EMPTY] * self.BOARD_SIZE for y in range(self.BOARD_SIZE)]

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

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

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

```diff_python
class Marubatsu:
    EMPTY = "."
    CIRCLE = "o"
    CROSS = "x"
-   BOARD_SIZE = 3

-   def __init__(self):
+   def __init__(self, board_size=3):
+       self.BOARD_SIZE = board_size
        self.initialize_board()

    def initialize_board(self):
-       self.board = [[Marubatsu.EMPTY] * Marubatsu.BOARD_SIZE for y in range(Marubatsu.BOARD_SIZE)]
+       self.board = [[Marubatsu.EMPTY] * self.BOARD_SIZE for y in range(self.BOARD_SIZE)]

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

    def display_board(self):   
-       for y in range(Marubatsu.BOARD_SIZE):
+       for y in range(self.BOARD_SIZE):
-           for x in range(Marubatsu.BOARD_SIZE):
+           for x in range(self.BOARD_SIZE):
                print(self.board[x][y], end="")
            print() 
```
</details>

上記のプログラムは、下記の部分を修正しています。

* __`BOARD_SIZE` 属性__ を __クラスの属性__ から、__インスタンスの属性__ に __変更__ したため、__クラスの `BOARD_SIZE` 属性__ への代入を __削除__ する
* __`__init__` メソッドの仮引数__ に ゲーム盤のサイズを代入する __`board_size`[^3] を追加__ する
* `__init__` メソッドのブロックの中で、__インスタンスの `BOARD_SIZE` 属性__ に __その値を代入__ する。なお、ゲームの __開始後__ にゲーム盤のサイズを __変更することはない__ ので、属性の名前は大文字と半角の `_` のみの名前にした
* __仮引数 `board_size`__ を __デフォルト値が `3`__ の __デフォルト引数__ にすることで、__実引数を記述せずに__ にインスタンスを作成すると、__3x3 のゲーム盤が作成される__ ようにする
* __`BOARD_SIZE` 属性__ を __クラスの属性__ から、__インスタンスの属性__ に __変更__ したため、__`Marubatsu.BOARD_SIZE`__ を、__`self.BOARD_SIZE` に修正__ する

下記のプログラムは、2 行目で __実引数を記述せず__ に `Marubatsu` クラスのインスタンスを作成しているので、`__init__` メソッドの __仮引数 `board_size`__ に __デフォルト値である `3` が代入__ されて __3x3__ のゲーム盤が作成され、3 行目でその表示が行われます。

一方、5 行目では __実引数に `5` を記述__ して `Marubatsu` クラスのインスタンスを作成しているので __5x5__ のゲーム盤が作成され、6 行目でその表示が行われます。

```python
1  print("3x3 のゲーム盤")
2  mb = Marubatsu()
3  mb.display_board()
4  print("5x5 のゲーム盤")
5  mb = Marubatsu(5)
6  mb.display_board()
```

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

```python
print("3x3 のゲーム盤")
mb = Marubatsu()
mb.display_board()
print("5x5 のゲーム盤")
mb = Marubatsu(5)
mb.display_board()
```
</details>

実行結果
```
3x3 のゲーム盤
...
...
...
5x5 のゲーム盤
.....
.....
.....
.....
.....
```

〇×ゲームでは、ゲーム盤のサイズを変更する必要はありませんが、せっかくなのでゲーム盤の __サイズを変更できるようにしたまま__ 実装を進めていくことにします。

### クラスの属性に対する docstring

クラスの定義に記述する docstring で、クラスの属性と、インスタンスの属性を区別して表記する方法を調べたのですが良くわからなかったので、本記事では下記のように記述することにします。

```
    Attributes:
        クラス属性
        EMPTY (str):
            空のマスを表す文字列
        （略）
            
        インスタンス属性
        BOARD_SIZE (int):
            ゲーム盤の縦横のサイズを表す整数
        （略）
```

## 値を変更するクラスの属性の利用例

ここまでの説明では、__値を変更しない__ ような __クラスの属性__ の __使用例__ を紹介しましたが、__値を変更する__ ようなクラスの属性の使用例についても紹介します。

例えば、__クラスの属性を利用__ することで、クラスから __作成したインスタンスの数__ を __数える__ ことができます。 

下記のように、__作成したインスタンスの数を数える__ ための __`count`__ という名前の __属性__ を __`Marubatsu` クラスに追加__ します。__クラスの定義を実行__ した時点では、インスタンスは __作成されていない__ ので、`count` には `0` を代入しています。この属性は、__値が変化する__ ので __名前は小文字__ にしています。

```python
Marubatsu.count = 0
```

クラスから __インスタンスを作成__ した場合は、__`__init__` メソッドが呼び出される__ ので、__`__init__` メソッドの中__ でこの __`count` 属性の値に `1` を足す__ という処理を記述します。__変数に値を加算__ する処理は、下記のプログラムの 2 行目のように、__`+=` という演算子を利用__ することが出来ます。

```python
1  def __init__(self, board_size=3):
2      Marubatsu.count += 1    # Marubatsu.count の値に 1 を加算する
3      self.BOARD_SIZE = board_size
4      self.initialize_board()
5
6  Marubatsu.__init__ = __init__
```

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

```python
def __init__(self, board_size=3):
    Marubatsu.count += 1    # Marubatsu.count の値に 1 を加算する
    self.BOARD_SIZE = board_size
    self.initialize_board()

Marubatsu.__init__ = __init__
```
</details>

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

```diff_python
def __init__(self, board_size=3):
+   Marubatsu.count += 1    # Marubatsu.count の値に 1 を加算する
    self.BOARD_SIZE = board_size
    self.initialize_board()
```
</details>

上記の修正によって、`Marubatsu` クラスから __インスタンスを作成するたび__ に、`Marubatsu` クラスの __`count` 属性の値が 1 ずつ増えていく__ ようになります。下記のプログラムの実行結果から、そのことを確認することが出来ます。

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

なお、〇×ゲームの実装を行う際に、`Marubatsu` クラスから作成したインスタンスを数える必要は特にないので、作成したインスタンスを数える処理は、github にアップロードする今回の記事の `marubatsu_new.py` には __反映しません__。

## クラスの属性のインスタンスからの利用

クラスの属性と __同じ名前の属性__ がインスタンスに __存在しない__ 場合は、__インスタンスから__ クラスの __属性を利用__ することが出来ます。先ほどのプログラムで `mb1` と `mb2` に代入された、`Marubatsu` クラスから作成された __インスタンス__ には、いずれも __`count` 属性が存在しない__ ので、下記のプログラムのように、__`mb1.count`__ と __`mb2.count`__ を表示すると、いずれも __クラスの `count` 属性__ の値である `2` が表示されます。

先程のプログラムで、`mb1 = Marubatsu()` を実行した直後に `print(Marubatsu.count)` を実行すると `1` が表示されるので、`mb1.count` には `1` が代入されていると __勘違いする__ 人がいるかもしれませんが、`count` は __クラスの属性__ なので、どのインスタンスから `count` 属性を表示しても __常に同じ数値__ が表示されます。

```python
print(mb1.count)
print(mb2.count)
```
実行結果
```
2
2
```

このようなことが起きる理由は、__インスタンスの名前空間__ に __属性名が登録されていない__ 場合は、インスタンスからクラスのメソッドを利用する際と __同様__ に、__クラスの名前空間__ から __名前を探す__ からです。

また、見つかった名前に対応づけられたオブジェクトが、__関数オブジェクトでない場合__ は、見つかったオブジェクトが __そのまま__ 名前に __対応づけられます__。

:::note
[前回の記事](https://qiita.com/ysgeso/items/44884dcf168aab949508#%E3%82%A4%E3%83%B3%E3%82%B9%E3%82%BF%E3%83%B3%E3%82%B9%E3%81%AE%E5%90%8D%E5%89%8D%E8%A7%A3%E6%B1%BA) で説明したように、見つかった名前に対応づけられたオブジェクトが __関数オブジェクトの場合__ に、メソッドオブジェクトが作られるという特殊な処理が行われるのは、インスタンスからメソッドを呼び出す際に、最初の仮引数 `self` にメソッドを呼び出したインスタンスを代入するという、通常の関数呼び出しでは行われない __特殊な処理が必要__ となるからです。

関数オブジェクト __でない__ 場合は、そのような特殊な処理は __必要ではない__ ので、見つかったオブジェクトがそのまま名前に対応付けます。
:::

だだし、このように __クラスの属性__ を __インスタンスの属性を記述して利用__ すると、プログラムの __意味が分かりづらくなる__ ので __避ける__ ことを個人的には __お勧めします__。

__特に__、インスタンスとクラスに __同じ名前の属性がある__ 場合に、プログラムで行われる処理が __非常にわかりづらくなる__ ことがあります。

具体例を挙げます。下記のプログラムは、2 行目で `Marubatsu` クラスの __インスタンスの `CIRCLE` 属性__ を、3 行目で、__`Marubatu` クラスの `CIRCLE` 属性を__ 表示しています。`Marubatsu` クラスの __インスタンス__ には、__`CIRCLE` 属性は存在しない__ ので、どちらも __`Marubatsu` クラスの `CIRCLE` 属性の値__ である `"o"` が表示されます。

一方、5 行目で、`Marubatsu` クラスの __インスタンスの `CIRCLE` 属性__ に `"丸"` を代入した後の 6、7 行目で、__2、3 行目と同じ処理__ を行うと、実行結果からわかるように、2、3 行目とは __異なる表示__ が行われます。

```python
1  mb = Marubatsu()
2  print(mb.CIRCLE)
3  print(Marubatsu.CIRCLE)
4
5  mb.CIRCLE = "丸"
6  print(mb.CIRCLE)
7  print(Marubatsu.CIRCLE)
```

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

```python
mb = Marubatsu()
print(mb.CIRCLE)
print(Marubatsu.CIRCLE)

mb.CIRCLE = "丸"
print(mb.CIRCLE)
print(Marubatsu.CIRCLE)
```
</details>

実行結果
```
o
o
丸
o
```

上記のプログラムをぱっと見た際に、多くの人は、2 行目の `mb.CIRCLE` が `Marubatsu` クラスの `CIRCLE` 属性を指すのだから、5 行目で、`mb.CIRCLE` に `"丸"` を代入すると、`Marubatsu` クラスの `CIRCLE` 属性に `"丸"` が代入されると __勘違いする人が多い__ のではないでしょうか？

実際には 5 行目の処理によって、インスタンスに __それまで存在しなかった__ `CIRCLE` 属性が __新しく作られる__ ことになります。その属性とクラスの同じ名前の属性は __異なる属性__ なので、5 行目の処理によってクラスの `CIRCLE` 属性の値は __変化しません__。

これは、「関数やメソッドの __ブロックの中__ で __変数に値を代入__ すると __ローカル変数__ に値が __代入される__ こと」、「__同じ名前__ のローカル変数とグローバル変数が __異なる変数__ であること」に似ています。

上記のような __紛らわしい処理__ は、プログラムの __バグの原因になる可能性が高い__ ので、__避けたほうが良い__ でしょう。

:::note warn
クラスとインスタンスの __属性名__ に関する __注意点__ は以下の通りです。

* __クラスの属性__ と __同じ名前__ を、__インスタンスの属性__ に __つけない__ ほうが良い

* __クラスの属性__ は、`クラスの名前.属性名` のように __記述したほうが良い__
:::

:::note
プログラムの書き方に関しては、人によって __さまざまな主張__ があります。例えば、`Marubatsu.CROSS` のような記述では、`Marubatsu` という名前は __グローバル名前空間__ が管理する名前なので、メソッドのブロック内の __ローカル名前空間のスコープ__ で __記述しないほうが良い__ という主張があります。これは [過去の記事](https://qiita.com/ysgeso/items/881dd00ab183aedd8cc4#%E9%96%A2%E6%95%B0%E3%81%AE%E3%83%96%E3%83%AD%E3%83%83%E3%82%AF%E3%81%AE%E4%B8%AD%E3%81%AE%E5%A4%89%E6%95%B0%E3%82%92%E8%A6%8B%E5%88%86%E3%81%91%E3%82%84%E3%81%99%E3%81%84%E3%83%97%E3%83%AD%E3%82%B0%E3%83%A9%E3%83%A0%E3%81%AE%E8%A8%98%E8%BF%B0%E6%96%B9%E6%B3%95) での「関数のブロックの中に、ローカル変数のみを記述することで、関数のブロックの中の変数が見分けやすくなる」という説明と同様の主張です。

個人的には、クラスの名前の __頭文字を大文字で記述__ することで、__クラスの名前__ であるかどうかが __簡単に区別できる__ などの理由から、メソッドのブロックの中で `Marubatsu.CROSS` のような記述を行っても問題はないと思います。

ただし、この手の主張には、一般的に __一長一短__ があるものなので、どの主張が正しいかを __一概に断言する__ ことは __できません__。興味がある方はそれぞれの主張を調べて比べてみて、自分が良いと思ったほうの主張を採用して下さい。
:::

# メソッドの種類

属性に、クラスの属性とインスタンスの属性の __2 種類__ があるように、メソッドには __インスタンスメソッド__、__クラスメソッド__、__静的メソッド__ の __3 種類__ があります。

## インスタンスメソッド

これまでの例で、クラスの定義の中で記述したメソッドは、__インスタンスから呼び出し__ て利用してきました。このように、__インスタンスから呼び出して利用__ するメソッドの事を __インスタンスメソッド__ と呼びます。インスタンスメソッドは以下のような特徴を持つメソッドです。

* __最初の仮引数__ `self` に、メソッドを呼び出した __インスタンスが代入__ される
* 一般的に、メソッドを呼び出した __インスタンスが管理する属性__ に対する __処理__ が行われる

本記事では、__単にメソッドと記述__ した場合は、__インスタンスメソッドの事を表す__ ことにします。

## クラスメソッド

__クラスメソッド__ は __クラスから呼び出される__ メソッドで、以下のような特徴を持ちます。

* メソッドの定義の直前の行に __`@classmethod`__ を記述する
* __最初の仮引数 `cls`__ に、メソッドを呼び出した __クラスが代入__ される
* 一般的に、メソッドを呼び出した __クラスが管理する属性__ に対する __処理__ が行われる

:::note
実際には、クラスメソッドはインスタンスから呼び出すこともできます。そのような場合に行われる処理については、この後で具体例を挙げて説明します。
:::

メソッドの定義の直前の行に記述する `@classmethod` は、__デコレータ__[^4] と呼ばれるもので、関数やメソッドに対して何らかの __機能を付け加える__（飾り付ける（decorate））という機能を持ちます。`@classmethod` はその直後で定義したメソッドを __クラスメソッドにする__ という機能を持ちます。

### クラスメソッドの使用例

クラスメソッドの使用例として、先ほどの「インスタンスを作成した数を数える」処理を __クラスメソッドで行う__ 例を紹介します。先ほどの例では、__`__init__` メソッドのブロックの中__ でクラスの属性 `count` に `1` を足すことで作成したインスタンスの数を数えていましたが、今回の例では、その処理をクラスメソッドで定義することにします。また、今回の例では __それに加えて__ インスタンスを __作成した際__ に、作成したインスタンスの数を表す __メッセージを表示する__ ことにします。

下記のプログラムでは、9 行目に __`@classmethod` を記述__ することで、10 ~ 14 行目で `add_count` という __クラスメソッドを定義__ しています。クラスメソッドの __最初の仮引数__ には、メソッドを呼び出した __クラスが代入__ されます。また、その仮引数の名前は __`cls`__ という名前を付ける[^5] __慣習__ があります。12、14 行目のように __`cls.count`__ を記述することで、__クラス__ の __`count` 属性__ に対する処理を行うことが出来ます。

:::note
`cls.count` の代わりに、`Marubatsu.count` と記述しても __全く同じ処理__ が行われますが、クラスメソッドでは、`cls.count` と記述したほうが良いでしょう。
:::

このクラスメソッドを、__`__init__` メソッドの中の 20 行目__ から呼び出すことで、インスタンスを作成するたびに、その数を数えて、メッセージを表示するという処理が行われます。

```python
 1  class Marubatsu:
 2      EMPTY = "."
 3      CIRCLE = "o"
 4      CROSS = "x"
 5      count = 0
 6
 7      # クラスメソッド `add_count` を定義する
 8      # 最初の仮引数 cls に、このメソッドを呼び出したクラスが代入される
 9      @classmethod
10      def add_count(cls):
11          # クラスの count 属性の値に 1 を足す
12          cls.count += 1
13          # メッセージを表示する
14          print(cls.count, "個目のインスタンスを作成しました")
15
16      # イニシャライザの中で `add_count` を呼び出すように修正した
17      def __init__(self, board_size=3):
18          self.BOARD_SIZE = board_size
19          self.initialize_board()
20          Marubatsu.add_count()  # クラスメソッド add_count を呼び出す
21
22      def initialize_board(self):
23          self.board = [[Marubatsu.EMPTY] * self.BOARD_SIZE for y in range(self.BOARD_SIZE)]
24
25      def place_mark(self, x, y, mark):
26          if self.board[x][y] == Marubatsu.EMPTY:
27              self.board[x][y] = mark
28          else:
29              print("(", x, ",", y, ") のマスにはマークが配置済です")
30
31      def display_board(self):   
32          for y in range(self.BOARD_SIZE):
33              for x in range(self.BOARD_SIZE):
34                  print(self.board[x][y], end="")
35              print()
```

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

```python
class Marubatsu:
    EMPTY = "."
    CIRCLE = "o"
    CROSS = "x"
    count = 0

    # クラスメソッド `add_count` を定義する
    # 最初の仮引数 cls に、このメソッドを呼び出したクラスが代入される
    @classmethod
    def add_count(cls):
        # クラスの count 属性の値に 1 を足す
        cls.count += 1
        # メッセージを表示する
        print(cls.count, "個目のインスタンスを作成しました")

    # イニシャライザの中で `add_count` を呼び出すように修正した
    def __init__(self, board_size=3):
        self.BOARD_SIZE = board_size
        self.initialize_board()
        Marubatsu.add_count()  # クラスメソッド add_count を呼び出す

    def initialize_board(self):
        self.board = [[Marubatsu.EMPTY] * self.BOARD_SIZE for y in range(self.BOARD_SIZE)]

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

    def display_board(self):   
        for y in range(self.BOARD_SIZE):
            for x in range(self.BOARD_SIZE):
                print(self.board[x][y], end="")
            print()
```
</details>

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

```diff_python
class Marubatsu:
    EMPTY = "."
    CIRCLE = "o"
    CROSS = "x"
    count = 0

    # クラスメソッド `add_count` を定義する
    # 最初の仮引数 cls に、このメソッドを呼び出したクラスが代入される
+   @classmethod
+   def add_count(cls):
+       # クラスの count 属性の値に 1 を足す
+       cls.count += 1
+       # メッセージを表示する
+       print(cls.count, "個目のインスタンスを作成しました")

    # イニシャライザの中で `add_count` を呼び出すように修正した
    def __init__(self, board_size=3):
        self.BOARD_SIZE = board_size
        self.initialize_board()
-       Marubatsu.count += 1
+       Marubatsu.add_count()  # クラスメソッド add_count を呼び出す

    def initialize_board(self):
        self.board = [[Marubatsu.EMPTY] * self.BOARD_SIZE for y in range(self.BOARD_SIZE)]

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

    def display_board(self):   
        for y in range(self.BOARD_SIZE):
            for x in range(self.BOARD_SIZE):
                print(self.board[x][y], end="")
            print()
```
</details>

下記のプログラムの実行結果から、インスタンスを作成するたびに、何回クラスメソッドを作成したかを表すメッセージが表示されることが確認できます。

```python
mb1 = Marubatsu()
mb2 = Marubatsu()
```

実行結果
```
1 個目のインスタンスを作成しました
2 個目のインスタンスを作成しました
```

### インスタンスからのクラスメソッドの呼び出し

インスタンスに __クラスメソッドと同じ名前の属性__ が __存在しない__ 場合は、下記のプログラムのように、インスタンスからクラスメソッドを呼び出すことが出来ます。

```python
mb1.add_count()
```
実行結果
```
3 個目のインスタンスを作成しました
```

:::note
インスタンスからクラスメソッドを呼び出した場合は、そのインスタンスを __作成したクラスから__ クラスメソッドを呼び出した場合と __同じ処理__ が行われます。

従って、クラスメソッドをクラスから呼び出した場合でも、インスタンスから呼び出した場合でも、最初の __仮引数 `cls`__ には __必ずクラスが代入__ されます。

インスタンスからクラスメソッドを呼び出した場合は、__見た目から__ インスタンスメソッドを呼び出しているか、クラスメソッドを呼び出しているかの __区別がつかない__ ので、__クラスメソッドはクラスから呼び出す__ ようにすることを __お勧めします__。
:::

## 静的メソッド

クラスの定義の中で、クラスの属性と、インスタンスの属性の __いずれに対しての処理を行わない__ メソッドを定義することが出来ます。そのようなメソッドの事を __静的メソッド__ と呼び、以下のような性質を持ちます。

* メソッドの定義の直前の行に __`@staticmethod`__ を記述する
* __最初の仮引数__ に特別な値は __代入されない__。__通常の関数呼び出しと同様の処理__ が行われる
* クラスやインスタンスの __属性__ に __影響を与えない__ が、__クラスに関連する処理__ が行われる

現時点では `Marubatsu` クラスに対して静的メソッドを定義すると便利な良い例が思いつかないので、具体例は示しません。何か静的メソッドを使う良い例が出てきた場合に具体例を説明しようと思います。

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

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

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

以下のリンクは、今回の記事で更新した marubatsu_new.py です。記事の中でも説明しましたが、作成したインスタンスの数を数える処理は、marubatsu_new.py には記述していません。

https://github.com/ysgeso/marubatsu/blob/master/021/marubatsu_new.py

# 次回の記事

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

[^1]:実際には、インスタンスによって 〇 のマーク表す文字列を変えるようなプログラムを記述することは不可能ではありませんが、そのようなプログラムを記述する利点はないでしょう
[^2]:とはいえ、すべてのデータを変数や属性に代入するのはさすがにやりすぎでしょう。利点が大きいと思ったデータを変数や属性に代入すると良いでしょう
[^3]:仮引数の名前を全て大文字にすることはあまりないと思いますので、小文字にしました
[^4]:デコレータは自作することもできます。デコレータの自作については、必要になった時点で紹介します
[^5]:`class` という名前にしないのは、`class` という名前が __クラスの定義__ を行う際に __別の用途で使われる__ からです