Pythonで〇×ゲームのAIを一から作成する　その19　オブジェクト指向プログラミング

# 目次と前回の記事

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

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


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

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

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

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

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

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

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

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

## 前回までのおさらい

前回の記事では、ゲーム盤の表示を行う、`display_board` という関数を定義しました。

# オブジェクト指向プログラミング

これまでに記述したプログラムでは、以下の変数と関数を定義しました。

* 変数
    * __board__：ゲーム盤を表すデータ

* 関数
    * __initialize_board__： 初期化されたゲーム盤のデータを返す
    * __place_mark__： 指定したマスにマークを配置する
    * __display_board__： ゲーム盤を表示する

現時点では、上記のように変数と関数の __数が少ない__ ので、混乱することは無いと思いますが、今後〇×ゲームの実装を進めていく際に、__新しい変数や関数__ をプログラムに __追加__ していくことになります。

一般的に、変数や関数などの __名前の数__ が数十、数百と __増えていった際__ に、それらの __名前の関連性__ が __わかりづらくなる__ という __問題が発生__ します。

また、一般的に、プログラムで扱う __データが増えた__ 場合、関数の __仮引数の数も増えるという傾向__ があります。例えば、関数呼び出しの際に、__実引数__ に __数十もの変数を記述__ するのは大変だと思いませんか？

このような問題は、__オブジェクト指向プログラミング__ によって解決することが出来る場合があります。

:::note
今回の記事を読んだだけでは、オブジェクト指向プログラミングの仕組みや便利さが良くわからないと思う人が多いかもしれません。今はわからなくても、オブジェクト指向プログラミングを行っていくにつれ、だんだんとその仕組みや便利さが実感できるようになると思います。

また、オブジェクト指向プログラミングには、他にも様々な利点があります。それらについて一度説明すると混乱すると思いますので、利点が実感できるような例が出てきた時点で説明します。
:::

## オブジェクト指向プログラミングとは何か

IT 用語辞典では、「オブジェクト指向プログラミング」は、以下のように説明されています。

『関連するデータの集合体と、それを操作する手続きを「オブジェクト」（object）と呼ばれるひとまとまりの単位として一体化し、オブジェクトの組み合わせとしてプログラムを記述する手法』

https://e-words.jp/w/%E3%82%AA%E3%83%96%E3%82%B8%E3%82%A7%E3%82%AF%E3%83%88%E6%8C%87%E5%90%91%E3%83%97%E3%83%AD%E3%82%B0%E3%83%A9%E3%83%9F%E3%83%B3%E3%82%B0.html

「[その 7](https://qiita.com/ysgeso/items/911fafe3f80a805165d1#python-%E3%81%AE%E3%83%87%E3%83%BC%E3%82%BF%E3%81%A8%E3%82%AA%E3%83%96%E3%82%B8%E3%82%A7%E3%82%AF%E3%83%88)」の記事では、Python の __オブジェクトの機能__ のうち、「__データを管理する__」という機能について説明しました。また、list のような __[複合データ型](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)__ によって、__オブジェクト__ が __複数のデータをまとめて管理__ する例について説明しました。オブジェクトが __複合データ型を扱うことが出来る__ ということが、上記の説明の「__関連するデータの集合体__」に対応します。

オブジェクトには、データを管理する以外の機能として、オブジェクトが __管理するデータに対する処理__ を行う、  __関数__ を __持つことが出来る__ という機能があります。オブジェクトが __関数の機能を持つことが出来る__ ということが、上記の説明の「__それを操作する手続き__」に対応します。

:::note
関数のように、複数の文をひとまとめにし、呼び出すことが出来るようにしたもののことを、プログラミング用語では __手続き__ と呼びます。
:::

オブジェクトを利用することで、__関連する複数のデータ__ と、それらのデータに対する処理を行う __複数の関数__ を __ひとまとまりの単位__ として扱うことが出来るようになります。そのような機能を持つ __オブジェクトを組み合わせて__ プログラムを記述する手法のことを「__オブジェクト指向プログラミング__」と呼びます。

これまで何度も利用してきた __list__ は、__複数のデータ__ を __ひとまとまりの単位__ として扱うことができる __オブジェクト__ です。また、list には、list が管理するデータに対して処理を行う __関数がいくつか定義__ されており、list の要素にデータを追加する __`append`__ は __list が持つ関数__ の一つです。


list だけでなく、整数型など、__Python が扱うデータ__ は __オブジェクトによって管理__ されており、それらのオブジェクトも __関数を持ちます__。例えば、文字列型のデータを管理するオブジェクトは、下記のプログラムのように、 __`upper`__ という名前の、文字列を大文字に変換した値を返す __関数を持ちます__。

```python
print("abc".upper())
```
実行結果
```
ABC
```

このように、Python の __データ__ は __オブジェクトで管理__ されているので、__Python でプログラムを記述すること自体__ が、オブジェクトの組み合わせとしてプログラムを記述する __オブジェクト指向プログラミング__ と言っても __間違いではありません__。ただし、__一般的__ には、__あらかじめ用意されている__ オブジェクト __だけを利用する場合__ は、オブジェクト指向プログラミングとは __言わない__ のではないかと思います。

Python では、list のような、複数のデータをまとめて扱い、それらに対してする処理を行う関数を持つ __オブジェクトを自作する__ ことが出来ます。具体的には、__任意のデータ構造__ を持ち、そのデータを __処理する関数__ を持つ __データ型を自作する__ ことが出来ます。一般的には、オブジェクト指向プログラミングは、__自作した__ オブジェクトを __組み合わせてプログラムを記述__ することを指します。




:::note
現在では、C++、JavaScript など、多くのプログラム言語でオブジェクト指向プログラミングを行えるようになっていますが、それぞれの言語で、__オブジェクトの仕組み__ や __用語__ などが __細かい点で異なる__ 場合があります。本記事では __Python のオブジェクト__ について説明しますが、他の言語を学ぶ際には、オブジェクトの使い方や用語の意味が異なる場合がある点に __注意して下さい__。
:::

## 属性とメソッド

Python のオブジェクトに関連する __重要な用語__ に、__属性__ と __メソッド__ があります。

属性には、インスタンス属性とクラス属性の 2 種類があり、今回の記事では __インスタンス属性__ について説明します。クラス属性については必要になった時点で説明します。また、インスタンス属性が最も良く使われる属性なので、今後の記事では __インスタンス属性__ のことを単に __属性__ と __表記__ することにします。

メソッドには、インスタンスメソッド、クラスメソッド、静的メソッドの 3 種類があり、今回の記事では __インスタンスメソッド__ について説明します。他のメソッドについては必要になった時点で説明します。また、インスタンスメソッドが最も良く使われるメソッドなので、今後の記事では __インスタンスメソッド__ のことを単に __メソッド__ と __表記__ することにします。


### 属性

Python では、オブジェクトが __管理するデータ__ の事を __属性（attribute）__ と呼びます。

Python のオブジェクトの属性は、以下のような性質を持ちます。

* オブジェクトが管理する __属性__ は、__属性名__ によって __識別__ する
* オブジェクトの属性は、__`オブジェクト.属性名`__ のように記述し、属性名の直前には __半角の `.`__（ピリオド）を記述する
* オブジェクトの属性は、以下のように __変数と同じ性質__ を持つ
    * 属性名は __変数名と同じルール__ [^1]で名前を付ける
    * 属性名は __名前空間__ によって __管理__ される（詳細は次回の記事で説明します）
    * `=` による __代入文__ で、__データを代入__ できる
    * __式の中で記述__ することで、属性に __代入された値を利用__ できる
    * 属性は、__代入されたデータ__ を管理する __オブジェクトを参照__ する

:::note
実際には、__`setattr`__ という組み込み関数を使って、属性名に __任意の文字列__ の __名前を付ける__ こともできますが、そのような名前を付けると `オブジェクト名.属性名` という方法で属性を記述することが __出来なくなります__。本記事ではそのような名前を属性名につけることはありません。
:::

:::note
JavaScript などでは、オブジェクトの __属性__ の事を __プロパティ__ と呼びますが、属性とプロパティは __異なる意味__ を持つ用語です。実際に Python では、オブジェクトの属性とプロパティは異なるものを表します。Python のプロパティの具体的な意味については、必要になった時点で説明します。
:::

### メソッド

オブジェクトが持つ __関数__ の事を、通常の関数と __区別__ して __メソッド（method）__ と呼びます。メソッドは、__`オブジェクト.メソッド名`__ のように記述します。関数とメソッドは __ほとんど同じ仕組み__ で処理が行われますが、__若干異なる__ 点があるので、メソッドの定義を管理するオブジェクトの事を関数オブジェクトと __区別__ して、__メソッドオブジェクト__ と呼びます。関数とメソッドの __違い__ の一つは、メソッドの場合は、`.メソッド名` が __記述されたオブジェクトが管理するデータに対して__ 処理が行われるという点です。

例えば、これまでの記事で作成した `place_mark` という __関数__ は、__実引数で記述したデータに対して__ 処理が行われますが、list の `append` __メソッド__ は __実引数で記述したデータ__ だけでなく、__`.append` が記述された list に対しても__ 処理が行われます。

:::note
__関数__ が、関数名と同じ名前の __変数__ に __関数の定義__ を表すデータが __代入__ されたものであるのと同様に、オブジェクトの __メソッド__ も、オブジェクトの __属性__ に __メソッドの定義__ を表すデータが __代入__ されたものです。__属性とメソッドの違い__ は、変数と関数の違いと同様に、__代入されているデータの種類の違いだけ__ です。属性とメソッドが __どちらも__ __`オブジェクト.名前`__ のように記述されるのはそのことが原因です。
:::

### まとめ

:::note warn
オブジェクトとメソッドに関するまとめは以下のようになります。

* オブジェクトが __管理するデータ__ の事を __属性__ と呼ぶ
* 属性は __属性名__ によって識別する
* 属性は __`オブジェクト.属性名`__ のように記述する
* 属性は __変数と同じ性質__ を持つ
* オブジェクトが持つ __関数__ の事を、__メソッド__ と呼ぶ
* メソッドは、__`オブジェクト.メソッド名`__ のように記述する
* メソッドを呼び出すと、メソッドを __呼び出したオブジェクト__ に __対して処理__ を行う
:::

## 組み込み型のクラスとインスタンス

Python では、数値型、文字列型、list、関数、モジュールなど、__変数に代入できるデータ__ は、__すべてオブジェクトが管理__ します。また、それらのデータを管理するオブジェクトは、__クラス（class）__ と呼ばれる、__ひな形__ となるオブジェクト __から作られます__。

__クラスから作成__ された、データを管理するオブジェクトの事を、__インスタンス__（instance）[^2] と呼びます。

この説明ではわかりづらいと思いますので、具体例を挙げて説明します。

:::note
__クラス__ と __インスタンス__ はどちらも __オブジェクトの一種__ なので、__クラスオブジェクト__ や __インスタンスオブジェクト__ のように表記する場合がありますが、表記が長くなるので、本記事では単に __クラス__ や __インスタンス__ のように表記します。
:::

### 組み込み型のクラス

数値型、文字列型、list など、Python で __あらかじめ定義__ されていて利用できるデータ型のことを __組み込み型__ と呼びます。組み込み型のデータは、__あらかじめ定義__ されている __クラスから作られます__。

:::note
Ptyhon の組み込み型の詳細については下記のリンク先を参照して下さい。
:::

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



組み込み型のクラスには、__データ型と同じ名前__ が付けられており、それらは組み込み関数の一種として __あらかじめ定義__ されています。例えば、整数型を表すクラスは `int`、文字列型を表すクラスは `str`、list を表すクラスは `list` です。

:::note
実際には __組み込みクラス__ と表記したほうが正確だと思いますが、下記の Python の公式ドキュメントでは、組み込みクラスを、__組み込み関数に含めている__ ので、上記ではそのように表記しました。

下記のリンク先の組み込み関数の説明の中で、`class int` のように、先頭に `class` が表記されているものが組み込みクラスです。
:::

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

### 組み込み型のインスタンス

組み込み型のクラスは、__通常の関数と同様__ に後ろに `()` を記述して __呼び出す__ ことができ、その返り値は実引数に記述した __組み込み型のデータ__ になります。例えば整数型の `1`、文字列型の `"abc"`、list の `[1, 2, 3]` は、それぞれ下記のプログラムのように記述することができます。

下記のプログラムのように、__クラスを呼び出すことで作成__ された __オブジェクト__ の事を __インスタンス__ と呼び、クラスからインスタンスを作成することを、__インスタンス化__ と呼びます。

```python
print(int(1))
print(str("abc"))
print(list([1, 2, 3]))
```
実行結果
```
1
abc
[1, 2, 3]
```

上記のプログラムは、下記のように記述することができるので、初めて上記のようなプログラムを見た人は、整数型の `1` をわざわざ `int(1)` のように記述する理由がわからないのではないかと思います。

```python
print(1)
print("abc")
print([1, 2, 3])
```
実行結果
```
1
abc
[1, 2, 3]
```

同じ組み込み型のデータを 2 通りの方法で記述できるのは、__組み込み型__ のデータは、プログラムで __頻繁に記述される__ ので、本来は `int(1)` のように記述する必要がある所を、`1` のようにクラスの名前と `()` を __省略して記述できる__ ようにしていることが原因です。従って、`1` のように記述しても、`int(1)` のように記述しても、`int` という __クラスをひな形__ に `1` という整数を管理する __インスタンスが作成される__ ことに変わりはありません。

:::note warn
これまでプログラムで記述してきた `1`、`"abc"`、`[1, 2, 3]` などの __組み込み型のデータ__ は、__すべて__ それらの組み込みデータ型を表す __クラスから作成__ された __インスタンス__ である。
:::

なお、この少し後で説明するような場合を除いて、一般的には、`1` のような組み込み型のデータをわざわざ `int(1)` のように記述する必要はありません。

### インスタンスの性質

クラスからは、インスタンスを __いくつでも__ 必要なだけ __作成__ することが出来ます。また、作成したインスタンスが管理するデータはそれぞれ __独立している__ ので、__それぞれ__ のインスタンスが __異なるデータを管理__ することが出来ます。

具体例を挙げます。下記のプログラムは、1 行目と 2 行目で `a` と `b` に __空の list__ を代入しています。プログラムで `[]` を記述した際に、__あらかじめ定義__ されている __list のクラス__ をひな形に、__空の list__ を管理する __新しい list のインスタンス__ が作成されます。`a` と `b` に代入された空の list は __異なるインスタンス__ なので、3 行目のように、`a` の list に要素を追加しても、`b` の値は __変化しません__。

```python
a = []
b = []
a.append(1)
print(a)
print(b)
```
実行結果
```
[1]
[]
```

厳密にはこの例えは正確ではないのですが、__クラス__ を __設計図__、__インスタンス__ を設計図から作成した __製品__ のように考えるとクラスとインスタンスの関係が理解しやすいのではないかと思います。例えば、本棚の設計図からいくらでも本棚を作ることが出来ますが、作られた __それぞれの本棚__ には __異なる本を入れる__ ことが出来ます。なお、クラスとインスタンスの仕組みについては次回の記事で説明します。


### 組み込み型の変換

`int(1)` のような方法で組み込み型のデータを記述する方法は、__データ型を変換__ する際に使われます。

Python では、一部の例外[^3]を除いて、異なるデータ型に対して `+` などの演算子で演算を行うとエラーが発生します。例えば、下記は数値型の `1` と文字列型の `"2"` を `+` 演算子で演算するプログラムで、実行するとエラーが発生します。

```python
print(1 + "2")
```
実行結果
```
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
c:\Users\ys\ai\marubatsu\019\marubatsu.ipynb セル 5 line 1
----> 1 print(1 + "2")

TypeError: unsupported operand type(s) for +: 'int' and 'str'
```

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

* __TypeError__
データ型（Type）に関するエラー
* __unsupported operand type(s) for +: 'int' and 'str'__
整数型（int type）と文字列型（str type）に対する（for）`+` 演算子（operand）による演算は提供されていない（unsupported）
:::

:::note
組み込み型の演算に関する詳細は、下記のリンク先を参照して下さい。
:::

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

このような場合は、__データ型を揃えて__ 演算を行う必要があります。あるデータ型のデータを __別のデータ型__ のデータに __変換__ することを __データ型の変換__ と呼び、Python では、データ型を表す __クラスの実引数__ にデータを記述して __呼び出す__ ことで、データ型を変換することが出来ます。

下記のプログラムでは、1 行目の `int("2")` によって文字列型の "2" を整数型の `2` に変換した結果、`1 + 2` が計算されて `3` が表示されます。2 行目の `str(1)` で整数型の `1` を文字列型の `"1"` に変換した結果、`"1" + "2"` が計算されて `12` が表示されます。

```python
print(1 + int("2"))
print(str(1) + "2")
```
実行結果
```
3
12
```

見た目からは区別できませんが、実行結果に表示されている `12` は数値型ではなく、文字列型の `"12"` を表しています。__組み込みデータ型の種類__ は __`type`__ という、実引数に記述したインスタンスを __作成したクラス__ を返り値として返す組み込み関数を使って調べることが出来ます。下記のプログラムの実行結果から、`1 + int("2")` の計算結果は整数型（int）、`str(1) + "2"` の計算結果は文字列型（str）のクラスから作成されたインスタンスであることを確認することが出来ます。

```python
print(type(1 + int("2")))
print(type(str(1) + "2"))
```
実行結果
```
<class 'int'>
<class 'str'>
```

### データ型の変換に関する注意点

データ型の変換がどのように行われるかについてはデータ型を表すクラスの種類によって異なります。例えば、下記のプログラムのように、数値型に変換することが出来ない `"a"` を整数型に変換しようとするとエラーが発生します。

```python
print(int("a"))
```
実行結果
```
---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
c:\Users\ys\ai\marubatsu\019\marubatsu.ipynb セル 8 line 1
----> 1 print(int("a"))

ValueError: invalid literal for int() with base 10: 'a'
```

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

* __ValueError__
値（Value）に関するエラー
* __invalid literal for int() with base 10: 'a'__
`"a"` は、10 を基数（base）とする（10 進数の事）数値型としては不正（invalid）なリテラル（literal）である
:::

クラス名を呼び出した際にどのような処理が行われるかについて詳しく知りたい方、下記のリンク先を参照して下さい。

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

### クラスとインスタンスのまとめ

:::note warn
ここまでで説明したクラスとインスタンスに関するまとめは以下のようになります。

* データを管理するオブジェクトは、__クラス__ と呼ばれる __ひな形__ となるオブジェクトから作られる
* クラスから作成されたオブジェクトの事を __インスタンス__ と呼ぶ
* __あらかじめ定義__ されていて利用できるデータ型のことを __組み込み型__ と呼び、組み込み型の __ひな形__ となる __クラス__ は __あらかじめ定義__ されている
* __組み込み型__ のデータを管理するオブジェクトは、その組み込み型の __クラス__ から作成された __インスタンス__ である
* インスタンスは __いくつでも作る__ ことが出来、インスタンスが管理するデータは __独立__ している
:::

## カスタムデータ型のクラスとインスタンス

関数を定義して自作できるのと同じように、オブジェクトが管理する __属性__ と、__メソッド__ に関する処理を記述した __クラスを定義__ して __自作する__ ことができ、そのようなクラスの事を __カスタムクラス__[^4] と呼びます。

__カスタムクラスを定義__ し、__インスタンス化__ する事で、任意のデータ構造を管理し、そのデータを処理するメソッドを持つ __カスタムデータ型__ を利用することが出来るようになります。

例えば、〇×ゲームに関する __データ__ を管理する __属性__ と、〇×ゲームに関する __処理__ を行う __メソッド__ を持つ __カスタムクラス__ を定義することで、__〇×ゲームを表すカスタムデータ型__ を利用することが出来ます。

表記が長くなるので、組み込み型のクラスと __区別する必要がない場合__ は、以後はカスタムクラスのことを単に __クラス__ と表記します。

### クラスの定義

クラスの定義は以下のように記述します。下記ではメソッドを 1 つだけ定義していますが、必要な数だけ、__複数のメソッドを定義__ する事もできます。また、クラスの定義の中でメソッドの定義以外の記述を行うこともできますが、今回の記事では説明しません。その点については次回の記事で説明します。

```python
class クラス名:
    def メソッド名(self, 仮引数):
        メソッドで行う処理を記述するブロック
```

:::note warn
__クラス名__ は、__他の名前__ と __一目で区別__ ができるように一般的に __頭文字が大文字の英語__ で記述します。頭文字が大文字でないクラス名をつけることもできますが、__避けたほうが良い__ でしょう。

ただし、あらかじめ定義されている __組み込み型__ のクラスの名前は __例外__ で、名前の __頭文字は小文字__ になっています。
:::

:::note
変数、関数、クラスなどの名前に、__日本語__ を使うことが __好まれない__ 原因の一つは、__クラスの名前__ であるかどうかを __一目で見分ける__ ことが出来るような名前の __工夫が難しい__ からです。
:::

メソッドの定義など、__クラスに関する__ プログラムの __記述__ は、クラスの __ブロックの中に記述__ します。

メソッドの定義は、通常の __関数の定義とほとんど同じ__ ですが、__最初の仮引数__ に __`self` を記述__ する点が異なります。`self` についてはこの後で詳しく説明します。

クラスのブロックに __処理を何も記述しない__ 場合は、ブロックの中に __pass のみの行__ を記述します。

下記は、__〇×ゲームのデータとメソッド__ を管理する __`Marubatsu`__ という名前の __クラスを定義__ しています。ただし、__現時点__ ではこのクラスには何の処理も記述していないので、このクラスは属性やメソッドを __持ちません__。この後でプログラムを追加していくことで、`Marubatsu` クラスを完成させていきます。

```python
class Marubatsu:
    pass
```

:::note
Python では、関数の定義がオブジェクトで管理されるのと同様に、__クラスの定義__ も __オブジェクトが管理__ します。クラスの定義を管理するオブジェクトのことを __クラスオブジェクト__ と呼びます。
:::

### インスタンス化

カスタムクラスから __インスタンスを作成__ する方法は、組み込み型のクラスからインスタンスを作成する場合と同様に、__クラス名の後に `()`__ を記述して __クラスを呼び出す__ ことで行います。`()` の中に __実引数を記述__ する場合こともできますが、その点については次回の記事で説明します。

カスタムクラスのインスタンス化の場合は、組み込み型で `int(1)` を `1` のように記述するような、__簡潔なインスタンス化__ の記述方法は __ありません__。


なお、__インスタンス化__ と __関数呼び出し__ は、__見た目__ だけでは __区別が困難__ なので __クラスの名前を大文字__ にして __区別できるようにする__ ことは非常に __重要__ です。


インスタンスは、__ひな形__ となる __クラスの定義で記述__ された __属性とメソッド__ を __利用することができます__。先ほどの `Marubatsu` クラスの定義には、属性もメソッドも __記述されていない__ ので、`Marubatsu()` を記述することによって作成されたインスタンスは、属性やメソッドを一つも持ちません。

:::note
クラスやインスタンスは、実際にはいくつかの __特殊属性__ と __特殊メソッド__ と呼ばれるものを持ちます。特殊属性と特殊メソッドは、クラスの定義に __記述しなくても__、オブジェクトが作られる際に __自動的に__ オブジェクトが管理する属性やメソッドに __組み込まれる__ ものです。特殊属性と特殊メソッドについては、この後で説明します。
:::

組み込み型の所で説明したように、__インスタンス__ はクラスから __いくつでも作る__ ことが出来ます。また、それぞれのインスタンスが __管理するデータ__ は __独立__ しています。下記のプログラムは、__`Marubatsu` クラスのインスタンス__ を __2 つ作成__ し、それぞれを `mb1` と `mb2` という __変数に代入__ しています。

なお、本記事では、`Marubatsu` のインスタンスを代入する __変数の名前__ は、maru と batsu の __頭文字__ をとって、__`mb` で始まる名前__ を付けることにします。


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

### 属性への値の代入と式の中での利用

`Marubatsu` クラスの定義には __何も記述されていない__ ので、`Marubatsu` クラスをひな形として作成されたインスタンスには、（特殊属性と特殊メソッドを除くと）__属性とメソッド__ は一つも __存在しません__。

先ほど説明したように、インスタンスの __属性__ は __変数と同じ性質__ を持ちます。従って、__値を代入__ することによって __変数が作られる__ のと同様に、__値を代入__ することによって、インスタンスに新しい __属性を作る__ ことが出来ます。また、__値が代入された属性__ は、変数と同様に __式の中で記述__ することで __代入された値を利用__ することが出来ます。

下記のプログラムでは、1、2 行目で __異なる__ `Marubatsu` のインスタンスが __代入された__ `mb1` と `mb2` の `a` という属性にそれぞれ `1` と `2` を代入しています。`mb1` と `mb2` は同じクラスから作られたインスタンスですが、それぞれは __独立したデータを管理する__ ので、3、4 行目の実行結果からわかるように、`mb1.a` と `mb2.a` には __異なるデータが代入__ されます。

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

:::note
__値が代入されていない変数名__ を式の中で記述すると NameError という __エラーが発生__ するのと同様に、下記のプログラムのように、__値が代入されていない属性名__ を式の中で記述すると、__AttributeError__ という __エラーが発生__ します。

```python
print(mb1.b)
```

実行結果
```
---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
c:\Users\ys\ai\marubatsu\019\marubatsu.ipynb セル 12 line 1
----> 1 print(mb1.b)

AttributeError: 'Marubatsu' object has no attribute 'b'
```
上記のエラーメッセージは、以下のような意味を持ちます。

* __AttributeError__
オブジェクトの属性（attribute）に関するエラー
* __'Marubatsu' object has no attribute 'b'__
Marubatsu というオブジェクト（object）は `b` という属性（attributi）を持たない（has no）
:::

### クラスのメソッドの定義

クラスのメソッドの定義は、「__クラスのブロック__ に記述する」、「__最初の仮引数__ に __`self` を記述__ する」以外の点では、__通常の関数と同じ方法__ で記述します。

__最初の仮引数 `self`__ には、その __メソッドを呼び出したインスタンスが代入__ されます。

具体例を挙げて説明します。下記のプログラムの 1 ~ 3 行目では、`Marubatsu` のクラスの定義の中で、__`initialize_board` メソッドを定義__ しています。従って、5 行目でこの `Marubatsu` クラスから作成されたインスタンスは、6 行目のように、__`initialize_board`__ という __メソッドを呼び出して利用することが出来ます__。6 行目で行われる処理の内容についてはこの後で説明します。

```python
1  class Marubatsu:
2      def initialize_board(self):
3          self.board = [["."] * 3 for y in range(3)]
4
5  mb = Marubatsu()
6  mb.initialize_board()
7  print(mb.board)
```

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

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

mb = Marubatsu()
mb.initialize_board()
print(mb.board)
```
</details>

実行結果
```
[['.', '.', '.'], ['.', '.', '.'], ['.', '.', '.']]
```

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

インスタンスのメソッドは __関数と同様の方法__ で、__`インスタンス.メソッド名(実引数)`__ を記述することで呼び出すことができます。ただし、通常の関数呼び出しとは以下の点が異なります。なお、実引数が記述された場合の具体例については、この次の `place_mark` の所で説明します。

* 呼び出されたメソッドの __仮引数 `self`__ に、__メソッドを呼び出したインスタンスが代入__ される
* __実引数__ が記述されている場合は、__`self` の後__ に記述された仮引数に対して __順番に代入__ される

上記のプログラムの、6 行目の __`mb.initialize_board()`__ を実行すると、__2、3 行目で定義__ された __`initialize_board`__ というメソッドが呼び出されます。その際に、__仮引数 `self`__ には、このメソッドを __呼び出したインスタンス__ が代入されている __グローバル変数 `mb` の値が代入__ されます。具体的には、下記のプログラムのような処理が行われると考えると良いでしょう。

```python
self = mb
self.board = [[" "] * 3 for y in range(3)]
```

従って、__`mb.initialize_board()`__ を実行すると、__`mb.board = [[" "] * 3 for y in range(3)]`__ と __同じ意味を持つ処理__ が行われ、__`mb.board`__ に __初期化されたゲーム盤が代入__ されます。

このように、__メソッドのブロックの中__ では、メソッドを呼び出した __インスタンスが管理するデータに対する処理__ を、__`self`__ を使って記述することが出来ます。従って、__インスタンスが管理するデータ__ を、メソッド呼び出しを行う際に、__実引数に記述する必要はありません__。

:::note
仮引数 `self` は、メソッドを呼び出したオブジェクト __自身__（self）を表すデータなので、self という名前が使われますが、これは __慣習__ なので、この仮引数の名前を `self` 以外の名前にすることもできます。ただし、`self` 以外の名前にするとプログラムの意味が非常にわかりづらくなるので __`self`__ を別の名前に __変更しない__ ようにすることを強くお勧めします。
:::

### メソッドの `initalize_board` と関数の `initialize_board` の違い

上記の `initialize_board` __メソッド__ と、これまでに定義した下記の `initialize_board` という __関数__ には、もう一つ __異なる点__ があります。それは、上記の `initialize_board` メソッドでは、__`board` という属性__ に初期化されたゲーム盤のデータを __代入__ しているのに対し、下記の関数の `initialize_board` では、初期化されたゲーム盤のデータを __返り値として返している__ 点です。その理由について説明します。

```python
def initialize_board() -> list[list[str]]:
    return [["."] * 3 for y in range(3)]
```

__関数__ の `initialize_board` は、下記のプログラムのように、初期化されたゲーム盤を __異なる複数の変数__ に __代入できるようにする__ ために、関数の __返り値__ に初期化されたゲーム盤のデータを __返していました__。

```python
board1 = initialize_board()
board2 = initialize_board()
```

一方、`Marubatsu` クラスから作成された __インスタンス__ は、それ自体が __1 つの__ 〇×ゲームに関するデータを管理する __オブジェクト__ です。そのため、`Marubatsu` クラスから作成された __インスタンスの中__ には、ゲーム盤のデータを代入する属性は __1 つだけあれば十分__ です。__複数のゲーム盤のデータ扱いたい場合__ は、下記のプログラムのように、Marubatsu から __複数のインスタンスを作成し__、__それぞれのインスタンスに対して__ `initialize_board` メソッドを呼び出すことで実現できます。

```python
1  mb1 = Marubatsu()
2  mb1.initialize_board()
3  print(mb1.board)
4  mb2 = Marubatsu()
5  mb2.initialize_board()
6  print(mb2.board)
```

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

```python
mb1 = Marubatsu()
mb1.initialize_board()
print(mb1.board)
mb2 = Marubatsu()
mb2.initialize_board()
print(mb2.board)
```
</details>

実行結果
```
[['.', '.', '.'], ['.', '.', '.'], ['.', '.', '.']]
[['.', '.', '.'], ['.', '.', '.'], ['.', '.', '.']]
```

上記のプログラムの 3、6 行目の実行結果から、`mb1` と `mb2` に代入された __インスタンスの `board` 属性__ に、初期化されたゲーム盤のデータが __代入__ されていることを確認することが出来ます。なお、この 2 つの `board` が __本当に独立したデータであるかどうか__ については、__この後で__ `place_mark` と `display_board` メソッドを定義してから __確認する__ ことにします。

### メソッドの定義とメソッドの呼び出しのまとめ

:::note warn
メソッドの定義とメソッドの呼び出しに関するまとめは以下のようになります。

* メソッドの定義は、クラスの定義のブロックの中に、__関数と同様の方法__ で記述する
* ただし、__最初の仮引数に `self` を記述__ する点が __異なる__
* __`self`__ には、メソッドを呼び出した __インスタンスが代入__ される
* メソッドは __`インスタンス.メソッド名`__ のように記述する
* メソッドの呼び出しは、__関数の呼び出しと同様の方法__ で記述する
* ただし、__実引数__ は、__`self` の後__ に記述された仮引数に対して __順番に代入__ される
:::

### `place_mark` と `display_board` メソッドの定義

`place_mark` と `display_board` __メソッド__ は下記のプログラムのように定義します。これまでに __関数として定義__ した `place_mark` と `display_board` との __違い__ は以下の通りです。

* クラスのブロックに記述する
* 最初の仮引数に `self` を記述する
* __仮引数 `board` を削除__ する
* ブロックの中の __`board`__ を __`self.board` に変更__ する
* メソッドの呼び出しの際に記述した __実引数__ は、__`self` の次の仮引数__ から __順番に代入__ される

具体的な違いについては、下記の修正箇所の部分をクリックして下さい。

```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):
            # y 行の各マスに対する繰り返しの処理を行う
            for x in range(3):
                # (x, y) のマスの要素を改行せずに表示する
                print(self.board[x][y], end="")
            # x 列の表示が終わったので、改行する
            print()  
```

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

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

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

-   def display_board(board):   
+   def display_board(self):   
        # 各行に対する繰り返しの処理を行う
        for y in range(3):
            # y 行の各マスに対する繰り返しの処理を行う
            for x in range(3):
                # (x, y) のマスの要素を改行せずに表示する
-               print(board[x][y], end="")
+               print(self.board[x][y], end="")
            # x 列の表示が終わったので、改行する
            print()  
```
</details>


最初の仮引数に `self` を記述する理由と意味については先ほど説明しました。

__仮引数 `board` を削除__ したのは、`initialize_board` メソッドを実行することによって、__インスタンスの `board` 属性__ にゲーム盤のデータが __代入される__ からです。__仮引数 `self`__ に、メソッドを呼び出した __インスタンスが代入__ されるので、__`self.board`__ が __ゲーム盤のデータ__ を表します。従って、__`board`__ を __`self.board` に変更__ することで、関数の場合と __同じ処理を行う__ ことが出来ます。

上記のクラスを定義する事で、下記の 2 ~ 5 行目のプログラムのように、上記で定義したメソッドの呼び出しを記述することが出来ます。3 行目の `place_mark(0, 1, "o")` のメソッドの呼び出しで記述した __実引数__ は、__`self` の次の仮引数__ である `x`、`y`、`mark` に __順に__ 代入されます。

実行結果から、(0, 1) のマスに 〇 が、(1, 2) のマスに × のマークが正しく配置されることが確認できます。

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

### 異なるインスタンスが独立したデータであることの確認

次に、`Marubatsu` から __複数のインスタンスを作成__ した際に、それらのインスタンスが __独立したデータ__ であることを __確認__ することにします。

下記のプログラムは、2、8 行目で、`Marubatsu` のインスタンスを作成し、それぞれを `mb1` と `mb2` に代入しています。3、9 行目でそれぞれのゲーム盤を初期化した後で、4 行目で `mb1` の (0, 1) のマスに 〇 を、10 行目で `mb2` の (1, 2) のマスに × のマークを配置しています。5、11 行目の実行結果から、`mb1` と `mb2` の `board` 属性に、__異なるゲーム盤のデータが代入__ されていることを確認することが出来ます。なお、1、7 行目の `print` は、表示する 2 つのゲーム盤の __境目をわかりやすくする__ ための表示です。

```python
 1  print("mb1")
 2  mb1 = Marubatsu()
 3  mb1.initialize_board()
 4  mb1.place_mark(0, 1, "o")
 5  mb1.display_board()
 6
 7  print("mb2")
 8  mb2 = Marubatsu()
 9  mb2.initialize_board()
10  mb2.place_mark(1, 2, "x")
11  mb2.display_board()
```

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

```python
print("mb1")
mb1 = Marubatsu()
mb1.initialize_board()
mb1.place_mark(0, 1, "o")
mb1.display_board()

print("mb2")
mb2 = Marubatsu()
mb2.initialize_board()
mb2.place_mark(1, 2, "x")
mb2.display_board()
```
</details>

実行結果
```
mb1
...
o..
...
mb2
...
...
.x.
```

## クラスを使うメリット

__`place_mark` と `display_board`__ を __クラスのメソッド__ として定義した場合と、__通常の関数__ で定義した場合の __違い__ は、メソッドの場合は 〇×ゲームに __関するデータが代入された変数__ `board` を __実引数に記述する必要がない__ という点です。

上記のプログラムと、同じ処理を行う、これまでに定義した関数を使ったプログラムを __比較__ して下さい。

```python
from marubatsu import place_mark, display_board

print("board1")
board1 = initialize_board()
place_mark(board1, 0, 1, "o")
display_board(board1)

print("board2")
board2 = initialize_board()
place_mark(board2, 1, 2, "x")
display_board(board2)
```
実行結果
```
board1
...
o..
...
board2
...
...
.x.
```

現時点では〇×ゲームに __関連するデータ__ が代入された __変数__ は、ゲーム盤のデータを代入する `board` のみであるため、あまり恩恵を感じられないかもしれませんが、__クラスを利用せず__ に〇×ゲームのプログラムを記述する場合は、今後「手番」や「勝敗結果」などを表す〇×ゲームに関する __データが増えた場合__ に、〇×ゲームの処理を行う __関数の仮引数が増えていく可能性が高く__ なります。

一方、__クラス__ を使って〇×ゲームの処理を行う場合は、__インスタンスの属性__ に __〇×ゲームに関連するデータ__ が __すべて保存__ されるようにしておけば、メソッドの __実引数__ に〇×ゲームに関するデータを記述する __必要が無くなります__。

また、クラスを使わない場合は、変数や関数が __増えた場合__ にそれらの __名前の関連性がわかりづらく__ なりますが、クラスを使った場合は、__関連する変数と関数__ が、インスタンスの __属性とメソッド__ で __まとめられる__ ので、それらの __関連性が明確__ になるというメリットがあります。

現時点では上記のメリットをあまり実感できないかもしれませんが、今後〇×ゲームの実装をクラスを使って進めていくことで、上記のメリットが徐々に実感できるようになるのではないかと思います。


## 特殊属性

クラスの定義を実行した際に作られるクラスオブジェクトや、クラスから作成されたインスタンスオブジェクトには、__クラスの定義__ で __記述されていない__、__特殊属性__ が自動的に作成されます。

特殊属性の名前は、前後に __半角のアンダースコア `_` を 2 つ並べた `__`__ が付けられるという決まりになっています。この `__` のことを、__ダブルアンダースコア__[^5] と呼びます。

:::note
特殊属性の名前の前後に `__` が付けられるのは、__他の目的__ でそのような名前を __定義する事がありそうにない__ からです。
:::

特殊属性をいくつか紹介します。他の特殊属性については必要になった時点で説明します。

### \_\_name\_\_

関数オブジェクト、クラスオブジェクト、メソッドオブジェクトなどで利用できる特殊属性で、関数、クラス、メソッドなどの __名前を表す文字列__ が代入されます。

単に `__name__` と記述した場合は、そのプログラムが記述されている __モジュールの名前__ が代入された特殊属性を表します。ただし、__メインモジュール__ の場合は、__`__main__`__ という __文字列__ が代入されます。

`__name__` 属性は、__実行した文__ が __メインモジュールのプログラム__ であるかどうかを __判定__ したい場合などで良く使われます。その使用法の具体例については必要になった時点で説明します。

```python
def add(x, y):
    """ x + y を計算する関数 """
    return x + y

print(add.__name__)  # add の関数名を表示する
print(__name__)      # この文を実行したモジュールの名前を表示する
```
実行結果
```
add
__main__
```

### \_\_dict\_\_

クラスやインスタンスなどで利用できる特殊属性で、__クラス__ と __インスタンス__ の場合は、__名前空間__ を表す dict が代入されます。詳細は次回の記事で説明します。

下記のプログラムは、__`Marubatsu` クラスの名前空間__ を表示しています。実行結果の意味については次回の記事で説明します。

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

## 特殊メソッド

__クラスの定義__ を記述する際に、__特殊メソッド__ という __特殊な処理を行うメソッドを定義__ することが出来ます。特殊メソッドの __名前__ は、__特殊属性と同様__ に、__前後に `__`__ が付けられます。

クラスの定義の中に、__特殊メソッドの定義を記述__ することで、クラスから作成した __インスタンス__ に対して __様々な機能__ を持たせることが出来るようになります。なお、特殊メソッドによって得られる機能が __必要ない__ 場合は、クラスの定義の中に特殊メソッドの定義を記述する __必要はありません__。

今回の記事では、__`__init__`__ という名前の特殊メソッドについて説明します。他の特殊メソッドについては、必要になった時点で紹介します。

クラスの特殊メソッドの一覧と詳細に関しては下記のリンク先を参照して下さい。

https://docs.python.org/ja/3/reference/datamodel.html#specialnames

### `__init__`

先程定義した `Marubatsu` クラスから作成されたインスタンスは、__`initialize_board` メソッドを呼び出す__ ことで __`board` 属性__ にゲーム盤のデータが __代入される__ ので、下記のプログラムのように、`initialize_board` メソッドを __呼び出す前__ に、`place_mark` や `display_board` メソッドを __呼び出す__ と、__`board` 属性__ に __値が代入されていない__ ので、__エラーが発生__ します。

```python
mb = Marubatsu()
mb.place_mark(0, 1, "o")
mb.display_board()
```
実行結果
```
---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
c:\Users\ys\ai\marubatsu\019\marubatsu.ipynb セル 23 line 2
      1 mb = Marubatsu()
----> 2 mb.place_mark(0, 1, "o")
      3 mb.display_board()

c:\Users\ys\ai\marubatsu\019\marubatsu.ipynb セル 23 line 6
      5 def place_mark(self, x, y, mark):
----> 6     if self.board[x][y] == ".":
      7         self.board[x][y] = mark
      8     else:

AttributeError: 'Marubatsu' object has no attribute 'board'
```

ゲーム盤の初期化処理は __最初に必ず行うべき処理__ なので、インスタンスを __作成した後__ で __自動的に__ `initialize_board` の __処理が行われる__ ようになると __便利__ です。

Python のクラスには、インスタンスを作成した後で __自動的に実行__ される __`__init__` という名前__ の、__イニシャライザ__ （初期化子) と呼ばれる __特殊メソッドを定義__ することが出来ます。

:::note
下記のリンク先の python の公式ドキュメントでは、`__init__` には特に名前は付けられていないようですが、筆者が調べた範囲ではイニシャライザと呼ばれる場合が多いような気がしましたので、本記事でもそのように表記します。
:::

https://docs.python.org/ja/3/reference/datamodel.html#basic-customization

なお、インスタンスを作成した後で自動的に実行したい処理が __存在しない場合__ は、クラスの定義の中で `__init__` の定義を記述する __必要はありません__。

:::note
`__init__` のことを、コンストラクタであると説明する場合が多いようです。コンストラクタ（constructor）とは、「建築」や「組み立て」を表す construct の名詞形で、クラスからインスタンスを作成する（組み立てる）際に実行されるメソッドの事を表します。Python の `__init__` は確かにコンストラクタと __似ています__ が、__厳密には異なる処理を行う__ ので、`__init__` のことをコンストラクタという名前で __呼ぶのは正確ではありません__。

Python では、コンストラクタに相当する処理を行う `__new__` という名前の特殊メソッドがありますが、`__new__` を使わなければならない場合は __あまり多くない__ ので、__一般的には `__init__` のほうが使われます__。本記事でも `__new__` は使用しません。

なお、Python の公式ドキュメントでは、クラスからインスタンスを作成する際に記述する `Marubatsu()` のような式の事を、コンストラクタ式と呼ぶようです。
:::

下記のプログラムは、`Marubatsu` クラスの定義の中で、__`initialize_board` メソッドを呼び出す処理__ を行う特殊メソッド __`__init__` を定義__ しています。

```python
class Marubatsu:
    def __init__(self):
        self.initialize_board()

    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):
            # y 行の各マスに対する繰り返しの処理を行う
            for x in range(3):
                # (x, y) のマスの要素を改行せずに表示する
                print(self.board[x][y], end="")
            # x 列の表示が終わったので、改行する
            print()  
```

上記のように `__init__` メソッドを定義する事で、`Marubatsu` のインスタンスを作成すると、__`__init__` メソッドが自動的に呼び出され__、その中で __`initialize_board` メソッドが呼び出される__ ようになります。従って、下記のプログラムの 2 行目のように、`Marubatsu` クラスからインスタンスを __作成した直後__ に `mb.place_mark` メソッドを呼び出しても __エラーは発生しなくなります__。

```python
mb = Marubatsu()
mb.place_mark(0, 1, "o")
mb.display_board()

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

:::note warn
インスタンスを作成した直後に __初期設定__ を行いたい場合は、__`__init__` メソッドを定義__ する。
:::

### `__init__` メソッドの仮引数

`__init__` メソッドの定義には __`self` 以外の仮引数を記述__ することが出来、インスタンスを作成する際に `()` の中に記述した __実引数が__ それらの __仮引数に代入__ されます。

具体例を挙げます。下記のプログラムは、`self` の次に __`x` という仮引数__ を持つ `__init__` メソッドが定義された `A` というクラスを定義しています。5 行目でクラス `A` のインスタンスを作成する際に、`()` の中の実引数に `5` を記述することで、`__init__` メソッドの __仮引数 `x` に `5` が代入__ されるので、インスタンスの __属性 `x`__ に __`5` が代入__ されます。従って、6 行目で `a.x` を表示すると `5` が表示されます。

```python
1  class A:
2      def __init__(self, x):
3        self.x = x
4
5  a = A(5)
6  print(a.x)
```

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

```python
class A:
    def __init__(self, x):
        self.x = x

a = A(5)
print(a.x)
```
</details>

実行結果
```
5
```

他の例としては、組み込み型のデータを `int(5)` のように、組み込み型のクラスに実引数を記述して呼び出すという方法で記述する際にこの仕組みが使われています。

`Marubatsu` クラスの `__init__` メソッドに仮引数を記述する例については次回の記事で紹介します。

## クラスに対する docstring

クラスに対する docstring は以下のように記述します。下記では省略していますが、クラスの中で定義されるメソッドの docstring は __通常の関数と同様の方法__ で記述します。ただし、メソッドの仮引数 __`self` の意味__ はどのメソッドでも __共通__ なので、__`self` に関する説明は省略__ します。

```python
class クラス名:
    """ クラスの概要

    クラスの詳細な説明

    Attributes:
        属性名 (属性のデータ型):
            属性の説明
```

下記は、`Marubatsu` クラスの定義に docstring を記述したプログラムです。

```python
class Marubatsu:
    """ 〇×ゲーム.

    Attributes:
        board (list[list[str]):
            ゲーム盤を表す 2 次元配列の list
            2 つのインデックスは、順に x 座標、y 座標を表す
            空白のマスは "."、〇 のマスは "o"、× のマスは "x" を代入する
    """

    def __init__(self):
        """ イニシャライザ """

        # ゲーム盤の初期化を行うメソッドを呼び出す
        self.initialize_board()

    def initialize_board(self):
        """ ゲーム盤のデータの初期化. """

        self.board = [["."] * 3 for y in range(3)]

    def set_mark(self, x: int, y: int, mark: str):
        """ ゲーム盤の指定したマスに指定したマークを配置する.

        (x, y) のマスに mark で指定したマークを配置する.
        (x, y) のマスに既にマークが配置済の場合は、メッセージを表示する.

        Args:
            x:
                マークを配置するマスの x 座標
            y:
                マークを配置するマスの 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):
            # y 行の各マスに対する繰り返しの処理を行う
            for x in range(3):
                # (x, y) のマスの要素を改行せずに表示する
                print(self.board[x][y], end="")
            # x 列の表示が終わったので、改行する
            print()   
```

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

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

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

以下のリンクは、今回の記事で更新した marubatsu_new.py です。

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

# 次回の記事

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

[^1]:「使える記号は `_` のみ」、「先頭にアラビア数字を使うことはできない」など
[^2]:「事例」を表す英単語です
[^3]:例えば、`[1] * 5` のように、list と整数型のデータを `*` 演算子で演算することができます
[^4]:「オーダーメイドの」、「注文して作った」を表す custom という英単語です
[^5]:英語では、double underscore。略してダンダー（dunder）と呼ぶこともあるようです