初学者のためのPython講座　オブジェクト指向編5　クラスの継承

ここからはクラスの応用について学習します。

クラスは型や設計図で実体がなく、実体であるインスタンスのほうが大事と感じるかもしれませんが、実はクラスの設計こそ重要であり、オブジェクト指向プログラミングのおもしろいところです。


# クラスの継承
オブジェクト指向を構成する概念の一つに**継承**があります。あるオブジェクトをベースに新たなオブジェクトを生成する、プログラムの再利用性を高める考え方です。
Pythonでは、すでに定義されたクラスの機能を引き継いで、新しいクラスの定義ができ、これを**クラスの継承**といいます。このとき、継承元になったクラスを**親クラス**または**スーパークラス**や**基底クラス**と呼び、新たに定義されたクラスは**子クラス**または**サブクラス**や**派生クラス**などと呼びます。本教材では**親クラス・子クラス**で統一します。

クラスの継承では、基本的な機能は元の親クラスから引き継ぎ、子クラスで必要な部分だけ書き換えたり、新たに機能を追加したりできます。これによって、クラスの拡張が容易になり、元になる親クラスの保守性も上がります。クラスを継承するには、継承したい親クラス名を引数として指定します。

####【書式】子クラスのクラス定義
```.py
class 子クラス名(親クラス名):
    クラスの内容
```

では、簡単に親クラスと子クラスを定義し、クラスの継承をプログラムで確認しましょう。

```inheritance.py

# 親クラスを定義する
class Human:
    def __init__(self, name, age):
        self.name = name
        self.age = age
    def status(self):
        print("氏名：" + self.name, "年齢：" + str(self.age) + "歳")

# 親クラスを継承した子クラスを定義する
class Human_say(Human):
    def introduce_myself(self):
        print("私は" + self.name + "です。" + str(self.age) + "歳です。")

# 子クラスのインスタンスを生成する
a_man = Human_say("荒木", 58)
# 親クラスのメソッドを呼び出す
a_man.status()
# 子クラスのメソッドを呼び出す
a_man.introduce_myself()
```

実行してみましょう。

```
氏名：荒木 年齢：58歳
私は荒木です。58歳です。
```

子クラスで生成されたインスタンスに親クラスのメソッドが引き継がれ、子クラスと親クラス両方のメソッドが実行できます。このように、クラスを継承することで簡単に機能を拡張していくことができます。

## メソッドのオーバーライド
クラス継承の際、親クラスで定義されているメソッドと同名のメソッドを定義することによって、メソッドを上書きすることができます。これをメソッドの**オーバーライド**と言います。
先ほどのプログラムの親クラスで定義されている`status()`メソッドを、オーバーライドしてみましょう。

```inheritance_over.py
class Human:
    def __init__(self, name, age):
        self.name = name
        self.age = age
    def status(self):
        print("氏名：" + self.name, "年齢：" + str(self.age) + "歳")

class English_Human(Human):
    # status()メソッドをオーバーライドする
    def status(self):
        print("NAME：" + self.name, "AGE：" + str(self.age))

# 親クラスのインスタンスを生成する
a_man = Human("荒木", 58)
# 子クラスのインスタンスを生成する
a_eng_man = English_Human("Araki", 58)
# メソッドを呼び出す
a_man.status()
a_eng_man.status()
```

実行してみましょう。

```
氏名：荒木 年齢：58歳
NAME：Araki AGE：58
```

親クラスと子クラスで生成されたインスタンスには、それぞれ同名の`status()`メソッドを実行していますが、子クラスはオーバーライドによって表示内容が変更されています。
**継承**は、同じようなプログラムをまとめて**再利用性**を高める考え方ですが、それだけでは似た振る舞いのプログラムしか作れないことになります。オーバーライドを利用することで、継承によって**共通化する部分は再利用**しつつ、**違う振る舞いにしたい部分は目的に合わせて変える**ことができます。


## ポリモーフィズムとは
このように、**同一のメソッド名で属するクラスによって異なる処理**が行われる性質を**ポリモーフィズム(多態性・多相性)**といい、オブジェクト指向において重要な考え方の一つです。
~~**継承**は、同じようなプログラムをまとめて**再利用性**を高める考え方ですが、それだけでは似た振る舞いのプログラムしか作れないことになります。**ポリモーフィズム**は、継承によって**共通化する部分は再利用**しつつ、**違う振る舞いにしたい部分は目的に合わせて変えられる**、オーバーライドを利用したものです。~~


例えば、テレビのリモコンは異なったメーカーのものでも、基本的な操作方法は共通しています。実際に内部で行われる処理はリモコンやテレビによって異なりますが、ユーザーは初めて触れるリモコンでも、数字のボタンでチャンネルを換えたり、ボリュームボタンを上に押せば音量を大きくできることがわかります。

つまり、異なるオブジェクト間に**共通のメソッド**を用意しておくことで、ユーザーはオブジェクトの内部を意識せずに**「この処理をするにはこのメソッドを使う」**ことがわかり、**オブジェクトによって処理内容を変化できる**ことが**ポリモーフィズム**です。これは、ユーザーだけでなくプログラミングにおいても、メソッド名を統一することで、名前を覚える必要がなくなること、記述ミスを減らせること、などが利点として挙げられます。


## 親クラスのメソッドを参照
ただし、意図せずメソッドの上書きをしてしまい、プログラムにエラーが発生することもあり、オーバーライドには十分注意が必要です。特に、親・子クラスともに初期化メソッドを使う場合、親クラスの初期値が上書きされ、値が参照できないことがあります。
組み込み関数の`super()`を使うことで、オーバーライドによって上書きされた**親クラスのメソッドを呼び出す**ことができます。

####【書式】super()関数で親クラスのメソッドを参照する
```.py
super().親クラスのメソッド
```

このとき、参照する親クラスのメソッドには第1引数の`self`は不要なので注意してください。
では、実際に`super()`関数で親クラスのメソッドを参照して、子クラスのメソッドを定義しましょう。

```inheritance_super.py
class Human:
    def __init__(self, name, age):
        self.name = name
        self.age = age
    def status(self):
        print("氏名：" + self.name, "年齢：" + str(self.age) + "歳")

class Human2(Human):
    # 初期化メソッドをオーバーライドする
    def __init__(self, name, age, blood, job):
        # 親クラスの初期化メソッドを参照する
        super().__init__(name,age)
        # 変数を追加する
        self.blood = blood
        self.job = job
    # status()メソッドをオーバーライドする
    def status(self):
        # 親クラスのstatus()メソッドを参照する
        super().status()
        # status()メソッドに追加する
        print("血液型：" + self.blood,"職業：" + self.job)

a_man = Human2("荒木", 58, "B", "漫画家")
a_man.status()
```

実行してみましょう。

```
氏名：荒木 年齢：58歳
血液型：B 職業：漫画家
```

親クラスの初期化メソッドと`super()`メソッドを参照し、子クラスではさらに独自のデータとメソッド内容を追加できました。

今回の例のように、小規模なプログラムではわざわざ`super()`関数で親クラスを参照せず、同じ内容をオーバーライドで書き直したほうがラクに思われるかもしれませんが、このように記述することで、**子クラスでは何を追加しようとしたのか**がわかります。継承したメソッドをオーバーライドする場合、継承する部分とオーバーライドする部分を明示的に記述するようにしましょう。


## 多重継承

Pythonでは複数のクラスから継承することも可能です。これを**多重継承**といいます。多重継承するには、継承したい複数の親クラス名を`,`（カンマ）で区切って指定します。

####【書式】多重継承によるクラス定義
```.py
class 子クラス名(親クラス名1, 親クラス名2, 親クラス名3 ... ):
    クラスの内容
```

では、簡単な多重継承の例を見てみましょう。
以下は、A、Bというクラスを定義して、多重継承によってA、Bのメソッドを持つクラスCを定義しています。 

![image.png](https://qiita-image-store.s3.amazonaws.com/0/270994/1b692123-ca2b-ae17-30e3-8b05be7763c6.png)


```inheritance_multi.py
class A:
    def sayA(self):
        print("Aクラス")
class B:
    def sayB(self):
        print("Bクラス")

# A,Bクラスを継承するCクラスを定義する
class C(A, B):
    pass

c = C()
c.sayA()
c.sayB()
```

実行してみましょう。

```
Aクラス
Bクラス
```

Cクラスから生成されたインスタンスに、クラスA、Bで定義されたメソッドが継承され、実行できました。

### 多重継承の優先順位
クラスがさらに増えるとどうなるでしょうか。次の例を見てみましょう。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/270994/32d1a341-4903-d98e-8ac3-96e6e719f743.png)


```inheritance_multi.py
class A:
    name = "Aクラス"
class B:
    name = "Bクラス"
class C:
    name = "Cクラス"
class D:
    name = "Dクラス"

class E(A,B):
    def say_name(self):
        print(self.name)
        print("Eクラスのメソッド")
class F(C,D):
    def say_name(self):
        print(self.name)
        print("Fクラスのメソッド")

class G(E,F):
    pass

g = G()
g.say_name()
```

クラスは多重継承が可能ですが、当然、**多重継承したクラスをさらに多重継承**、ということもできます。また、継承で同名の変数やメソッドがある場合は上書き(オーバーライド)されます。
しかし、この例のように多重継承によって変数やメソッドの名前がぶつかった場合、どちらが優先されるでしょうか？プログラムを実行して確認してみましょう。

```
Aクラス
Eクラスのメソッド
```

Pythonでは、多重継承によって名前がぶつかった場合、**多重継承に指定したクラスの順番で優先されます**。つまり、**指定した左側のクラスが優先される**ため、`say_name()`メソッドは**Eクラス**、メソッドが参照している変数`name`は**E→Aクラス**のものを読み込みます。
もし、Gクラスが`class G(E,F)`ではなく`class G(F,E)`と、多重継承するクラス名を逆に記述した場合はどうなるでしょうか？今度は、Fクラスが優先されるため`say_name()`メソッドはFクラス、変数`name`はF→Cクラスに読み込みます。もし、Cクラスに変数`name`が存在しない場合は、Dクラスを参照します。
このように多重継承は指定したクラス名の**記述順で優先順位が決まる**ため、注意が必要です。


### 多重継承の問題点
一見すると、多重継承はとても自由度が高く便利な機能ですが、先ほどの例のように、多重継承の多重継承というクラスを作ってしまうと、構造が複雑化し参照元のメソッドや変数の把握が難しくなります。
また、名前の衝突によって意図しない上書きも起こりやすく、予期しないエラーを招く危険性があります。
これを回避するためにも、わかりやすい名前付けや`super()`関数の利用は有効ですが、何よりも多重継承が招く問題を把握し、必要最小限度の利用を心がけるようにしましょう。


***
[初学者のためのPython講座　オブジェクト指向編1　オブジェクト指向とは](https://qiita.com/kotakahe/items/9de3b515f8faf01510f0)
[初学者のためのPython講座　オブジェクト指向編2　クラスとインスタンス](https://qiita.com/kotakahe/items/3de8d471ea871b0a7024)
[初学者のためのPython講座　オブジェクト指向編3　メソッド](https://qiita.com/kotakahe/items/3360898ff84c2c141a1b)
[初学者のためのPython講座　オブジェクト指向編4　初期化メソッド](https://qiita.com/kotakahe/items/db3e9fa9d547bb6e9014)

随時追加予定