初学者向けPythonのクラスの使い方

## はじめに
エンジニアの新人研修にて、オブジェクト指向の理解がひとつのハードルになることが多いです。
初心者向けにPythonによるクラスの使い方を紐解きながらオブジェクト指向の概要を説明します。

## 1.クラスの概要
Pythonは、Javaなどと同じオブジェクト指向プログラミングが可能です。
オブジェクト指向プログラミングで重要な概念の一つである「クラス」について説明します。

そもそもクラスとは何かと言うと、何か「モノ」を作るための「設計図」や「型」の様なものです。
設計図であるクラスには、**属性(プロパティ)** と **操作(メソッド)** を定義します。
作成したクラスという設計図を元にして、実際に作った「モノ」を**インスタンス**と言います。

## 2.クラスの定義
今回は「人間」というクラスを定義する方法について考えていきます。

まず、「人間」というクラスを定義するにあたって、「人間」というものを表すにはどんな情報が必要なのかを考えます。
例えば、次の様な情報が考えられます。

「人間」を表すのに必要な情報。
- 名前
- 年齢

上記の「名前」や「年齢」が「属性(プロパティ)」と呼ばれるものになります。
この情報を元に、クラスを作成してみましょう。

```python
class Person: # 人間クラスの定義
    #コンストラクタ。クラスがもつ属性の定義と初期化を行う。
    def __init__(self,name,age):
        self.name = name
        self.age = age
        
```
Pythonでは、クラスを作成する際に「class クラス名」で定義をします。
クラスに属性がある場合は、コンストラクタと呼ばれるメソッドで属性の定義と初期化を行います。
属性の定義というのは、「このクラスはこんな属性を持っていますよ」と宣言することです。
初期化と言うのは、そのクラスが持っている属性に最初の値を設定する作業のことです。
コンストラクタはクラスの中で一番最初に呼ばれるメソッドになります。

## 3.インスタンスの作成
前項でクラスの定義が一旦完成しました。
このクラスを使って、実際に「人間」という「インスタンス(実体)」を作ってみます。

```python
person1 = Person('Yamada',24) # インスタンスの作成
print(person1.name) # 名前を表示
```
**実行結果**
```
Yamada
```
クラスを使って人間のインスタンスを作る際に、クラスに定義されている属性である名前と年齢に値を設定する必要があります。
今回は「名前はYamada、年齢は24」という情報をPersonクラスに与えました。
Personクラスは、この情報を元にコンストラクタを呼び出し、人間を一人作ります。
このように、名前と年齢をそれぞれ指定することで、複数の人間を作成することもできます。
```python
person2 = Person('Suzuki',19) # 名前はSuzukiで年齢は19才の人間を作成。
person3 = Person('Tanaka',40) # 名前はTanakaで年齢は40才の人間を作成。
print(person2.name)
print(person3.name)
```
```
Suzuki
Tanaka
```
Personクラスは「人間とはどういうものか」を定義するものであって、人間そのものではありません。
Personクラスという設計図を元にして作った「インスタンス」が人間そのものになります。

## 4.メソッドの概要
メソッドとは、そのクラスがどういう「**動き**」を持つのかを定義したものです。
前回と同様、人間というクラスを定義します。
```python
class Person: # 人間クラスの定義
    #コンストラクタ。クラスがもつ属性の定義と初期化を行う。
    def __init__(self,name,age):
        self.name = name
        self.age = age
        
```
上記のクラスは、属性の定義と初期化を行うコンストラクタが定義されています。
このコンストラクタもメソッドの一つです。
コンストラクタ以外にも、クラスには自分で考えたメソッドを定義することができます。

## 5.メソッドの定義
人間はどんな「動き」を持っているのか考えてみましょう。
例えば、次のような「動き」が考えられます。

- 自己紹介をする
- 挨拶をする
- 名前を答える
- 年齢を答える

これらの動きをメソッドで表してみます。
```python
class Person: # 人間クラスの定義
    #コンストラクタ。クラスがもつ属性の定義と初期化を行う。
    def __init__(self, name, age):
        self.name = name
        self.age = age

    # 自己紹介をする。
    def selfIntroduction(self):
        print('Hello! My name is ', self.name, ', I am ', self.age, ' years old.')

    # 挨拶をする。
    def greet(self, greetWord):
        print(greetWord)

    # 名前を答える。
    def answerName(self):
        return self.name

    # 年齢を答える。
    def answerAge(self):
        return self.age

```
メソッドは <strong>`def メソッド名(self, 引数):` </strong>の形式で定義を行います。
**末尾の「：(コロン)」を忘れないよう注意してください。**

「**引数(ひきすう)**」とは、メソッドや関数に渡す値のことです。
コンストラクタも、nameとageという引数を定義しています。
引数は、メソッドに必ず必要なものではなく、メソッドの処理に必要ない場合は省略が可能です。
また、カンマで区切って複数の引数を定義することもできます。
selfというのは自クラスのインスタンスになります。これは**省略することができません。**

メソッドには、「**戻り値**」というものを定義することもできます。
戻り値とは、メソッドで処理した結果を返却する値のことです。
値を返却する必要がなく、メソッド内で処理が完結する場合は、引数と同様に省略することが可能です。

まとめると、メソッドは以下のパターンで定義することができます。
| 引数 | 戻り値 |
|:-:|:-:|
| なし | なし |
| あり | なし |
| なし | あり |
| あり | あり |

## 6.メソッドの利用
前回も述べた通り、Personクラスは「人間とはどういうものか」を定義するものであって、人間そのものではありません。
Personクラスという設計図を元にして作った「インスタンス」が人間そのものになります。
つまり、クラスの中のメソッドも、クラスに定義しただけでは使えないのです。
クラスに定義したメソッドを、インスタンスを介して呼び出すことで初めて利用することができます。
```python
person1 = Person('Yamada',24) # インスタンスの作成
person1.selfIntroduction()
# 実行結果
# Hello! My name is Yamada, I am 24 years old.
```
メソッドを呼び出す際には、 <strong>`インスタンス変数名.メソッド名()` </strong>の形式で呼び出すことができます。
「person1」がインスタンス変数名になります。
インスタンス変数名の後に、メソッド名を指定して呼び出すことで、そのメソッドでの処理が行われます。

メソッドは、 戻り値や引数の有無で呼び出し方が異なります。
呼び出す際に、selfの引数は必要ありません。

| 引数 | 戻り値 | メソッドの呼び出し方 |
|:-:|:-:|:-|
| なし | なし | インスタンス変数名.メソッド名() |
| あり | なし | インスタンス変数名.メソッド名(引数に設定する値) |
| なし | あり | メソッドの戻り値を格納する変数=インスタンス変数名.メソッド名() |
| あり | あり | メソッドの戻り値を格納する変数=インスタンス変数名.メソッド名(引数) |
```python
person1 = Person('Yamada',24) # インスタンスの作成
# 色々なメソッドの呼び出し方
person1.selfIntroduction()
person1.greet('こんにちは')
person1.answerName()
person1.answerAge()
```
**実行結果**
```python
Hello! My name is Yamada, I am 24 years old.
こんにちは
Yamada
 24
```
このように、メソッドを定義することで様々な処理を行うことができます。

初めのうちは、メソッドを定義する際に、引数や戻り値の有無が判断できずに少し戸惑うかもしれませんが、「どんな動きをすればいいか」を考えて、その動きを実現するためには別途情報が必要か(※引数の有無)、結果を返す必要があるのか(※戻り値の有無)を考えていくと、自ずと判断できるようになってきます。

## 7.継承の概要
これまで人間クラスを定義しました。
ここからは、社会人という新しいクラスを定義します。
社会人というクラスには、どんな属性が必要でしょうか。
例えば、次の様な情報が考えられます。

- 名前
- 年齢
- 所属会社

上記の「名前」や「年齢」は、人間クラスと同じ属性です。
このままクラスを作ると、同じような構造を持ったクラスが重複してしまいますし、あまり効率的とは言えません。

そこで、人間クラスを元として、人間クラスの持つ属性を受け継いだ社会人クラスを作成することとします。
このように、基本となるクラスを元にして、基本となるクラスの属性を受け継いだ新しいクラスを作ることを「<font color="FireBrick">**継承**</font>」と言います。

継承元となるクラスの事を、「<font color="FireBrick">**スーパークラス**</font>」、継承元を受け継いで作ったクラスの事を「<font color="FireBrick">**サブクラス**</font>」と言います。
今回は人間クラスがスーパークラスで、社会人クラスがサブクラスになります。
また、スーパークラスとサブクラスは、親子関係に例えることもあります。
この場合、人間クラスが親クラス、社会人クラスが子クラスになります。
スーパークラスは、複数のサブクラスの継承元になることができます。
サブクラスは、複数のスーパークラスを継承元として作成でき、これを「<font color="FireBrick">**多重継承**</font>」と言います。
多重継承については後述します。

## 8.継承を利用したクラスの定義
継承を利用したクラスは下記のように定義をします。
```python
class Society(Person): 
    # 人間クラスを継承した社会人クラスの定義
    # コンストラクタ。スーパークラスが持つ属性と、サブクラスのみが持つ属性の初期化を行う。
    def __init__(self, name, age, company):
        super().__init__(name, age) # スーパークラスのコンストラクタの呼び出し
        self.company = company      # サブクラスの属性の初期化

```
継承を利用するクラスは、<code>**class クラス名(スーパークラス名):**</code> の形式で定義を行います。
コンストラクタに、サブクラスのみが持つ属性を定義することができます。
その際に、「super().\_\_init\_\_(スーパークラスのコンストラクタに渡す引数(複数指定可))」と記述することで、スーパークラスのコンストラクタを呼び出すことができます。

## 9.継承を利用したメソッドの定義
サブクラスは、スーパークラスの属性だけではなく、メソッドも受け継いでいます。
スーパークラスのメソッドをそのまま使用する場合は、サブクラスに定義しなおす必要はありません。
サブクラスにメソッドを定義するのは、以下のような場合です。

- サブクラスでスーパークラスのメソッドを再定義する
- スーパークラスのメソッドに、サブクラスで処理を付け足し、拡張する
- サブクラス独自のメソッドを追加する
```python
class Society(Person): # 人間クラスを継承した社会人クラスの定義
    # コンストラクタ。スーパークラスが持つ属性と、サブクラスのみが持つ属性の初期化を行う。
    def __init__(self, name, age, company):
        super().__init__(name, age) # スーパークラスのコンストラクタの呼び出し
        self.company = company      # サブクラスの属性の初期化

    # スーパークラスの「自己紹介をする」メソッドを再定義する。
    def selfIntroduction(self):
        print('私の名前は', self.name, 'です。年齢は', self.age, '歳です。所属会社は', self.company, 'です。よろしくお願いします。')  
 
    # スーパークラスの「挨拶をする」メソッドに処理を付け足す。
    def greet(self, greetWord):
        super().greet(greetWord)
        print('お元気ですか？')

    # サブクラス独自のメソッドを追加する。
    def answerCompany(self):
        return self.company

```
スーパークラスで定義したメソッドを、サブクラスで再定義することを「<font color="FireBrick">**オーバーライド**</font>」と言います。
オーバーライドをする際は、<font color="FireBrick">**スーパークラスで定義したメソッドと同じメソッド名で定義をする必要があります。**</font>

また、サブクラスでスーパークラスのメソッドに処理を付け足す場合、オーバーライドと同様に、スーパークラスで定義したメソッドと同じメソッド名で定義を行い、「super().スーパークラスのメソッド名(引数)」でスーパークラスのメソッドを呼び出す必要があります。

## 10.サブクラスのメソッドを呼び出す
サブクラスのメソッドは、サブクラスのインスタンスを作成して呼び出すことができます。
```python
soc1 = Society('田中',22,'●●会社') # サブクラスのインスタンスの作成

# サブクラスでオーバーライドしたメソッドの呼び出し。
soc1.selfIntroduction()

# サブクラスで処理を付け足して拡張したメソッドの呼び出し。
soc1.greet('こんにちは')

# サブクラスで新しく定義したメソッドの呼び出し。
print('所属会社：',soc1.answerCompany())

# 実行結果
# こんにちは、私の名前は田中です。年齢は22歳です。所属会社は●●会社です。よろしくお願いします。
# こんにちは
# お元気ですか？
# 所属会社： ●●会社
```

## 11.クラスの多重継承
Pythonでは、サブクラスを定義する際、継承元であるスーパークラスを複数指定することができます。これを「<font color="FireBrick">**多重継承**</font>」と言います。
多重継承は、サブクラスを定義する際に「class クラス名(スーパークラス名1,スーパークラス名2,…):」と記述します。

継承を利用すると、同じようなクラスをいくつも定義する必要がなくなったり、関連性を持ったクラスを紐付けることができます。
新しいクラスを作成する際は、既に定義したクラスから継承できないかを考慮しながら作成できると、より効率的なプログラミングが行えるようになります。

## さいごに
最後までご覧頂きありがとうございました。Python学習の参考になれば幸いです。