【Python】オブジェクト指向プログラミング基礎vol.4~クラスの継承~

## 継承(Inheritance)

**既存のクラスが持つ変数とメソッドを引き継いで（継承して）、新しいクラスをつくる仕組み**

```python
class Diagonal:
    def __init__(self):
        self.name = 'Class Diagonal'
        
class Straight(Diagonal):
    pass

s = Straight()
print(s.name)
```

実行結果

```
Class Diagonal
```

実行すると、`Class Diagonal`という文字が表示される。`Class Straight`には`name`という変数宣言はないがDiagonalクラスで定義された変数にStraightクラスがアクセスできている。

`class Straight(Diagonal)`とクラス名の隣にかっこで他のクラス名を入れることで、上の場合だとStraightクラスはDiagonalクラスを**継承**するという意味になる。

継承元のクラス(Diagonalクラス)のことを**スーパークラス(親クラス)**といい、Straightクラスは**サブクラス(子クラス)**になる。



## オブジェクトのデータメンバーとメソッドの一覧

あるオブジェクトのデータメンバーとメソッドのリストは、`dir(オブジェクト)`として取得できる。

`print(dir(s))`とすると下記のように出力され、オブジェクトStraightに`name`があることが確認できる。

```
['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'name']
```



## メソッドの追加

子クラスで関数を追加することができる。子クラスからは、親クラスのメソッドも呼べる。

子クラスが親クラスの機能を含む。

```python
class Diagonal:
    diag = "diagonal"
    
    def funcD(self):
        print('func D')
        

class Straight(Diagonal):  # DiagonalClassを継承
    def funcS(self):
        print('func S')
        
	def diag_print(self):
        print(self.diag)
     

s = Straight()
s.funcD()
s.funcS()
s.diag_print()
```

実行結果

```
func D
func S
diagonal
```

このように継承を用いると、継承元のクラスの変数とメソッドを継承先のクラスで利用できる。

→**既存のクラスを修正せずに機能追加が可能になり、保守性・再利用性が向上する**



また継承は、**クラスの共通部分を別クラスとしてまとめる仕組み**ともいうことができる。

以下の2つのクラスを見てみると、プログラムの内容に共通する部分が多いことがわかる。

```python
class Soccer_player:
    def __init__(self, name):
        self.name = name
        
    def hello(self):
        print("私の名前は{}です".format(self.name))
        
    def shoot(self, field):
        print("{}で点を決めた".format(field))
        
class Baseball_player:
    def __init__(self, name):
        self.name = name
        
    def hello(self):
        print("私の名前は{}です".format(self.name))
        
    def home_run(self, field):
        print("{}でホームランを打った".format(field))
```

継承を使うとこのコードの共通部分をまとめることができる。

```python
class Sports_player:
    def __init__(self, name):
        self.name = name
    
    def hello(self):
        print("私の名前は{}です".format(self.name))
        
        
class Soccer_player(Sports_player):
    def shoot(self, field):
        print("{}は{}で点を決めた".format(self.name,field))
        
class Baseball_player(Sports_player):
    def home_run(self, field):
        print("{}は{}でホームランを打った".format(self.name,field))

        
s = Soccer_player("クリステファン・ローディ")
b = Baseball_player("バーズ")
s.hello()
s.shoot("日産スタジアム")
b.hello()
b.home_run("東京ドーム")
```

実行結果

```
私の名前はクリステファン・ローディです
クリステファン・ローディは日産スタジアムで点を決めた
私の名前はバーズです
バーズは東京ドームでホームランを打った
```

Soccer_playerクラスとBaseball_playerクラスはSports_playerクラスを継承している。

２つのクラスの共通部分をSports_playerクラスにまとめた上で、Sports_playerクラスを敬称している



共通部分をまとめると、以下のようなメリットがある

- プログラムの記述量が少なくなり、見通しが良くなる
- 他のスポーツ選手のクラスを定義するのが簡単になる
- 共通部分の変更が容易になる

共通部分をまとめることで、**プログラムの保守性・再利用性を向上**させている



## オーバーライド（override）

継承では同じメソッドがあると、内容を上書きして差し替えることが可能。

```python
class Diagonal:
    def hello(self):
        print('Hello Class Diagonal')
        
class Straight(Diagonal):
    def hello(self):
        print('Hello Class Straight')
        
        
s = Straight()
s.hello()
```

実行結果

```
Hello Class Straight
```

上のように`Hello Class Straight`と表示される。自分自身の定義が先で、なかったら親を探しにいく。親にもなければさらにその上へと探しに行く。



## super関数

親クラスのメソッドに、子クラスからアクセスするときに使うのが、**super関数**。

`super().親クラスのメソッド(引数)`のように記述する。

```python
class Diagonal:
    def hello(self):
        print('Hello Class Diagonal')
        
class Straight(Diagonal):
    def hello(self):
        super().hello()
        print('Hello Class Straight')
        
        
s = Straight()
s.hello()
```

実行結果

```
Hello Class Diagonal
Hello Class Straight
```

上記の例だと、Diagonalクラスのhelloを呼び出してから、Straightクラスのhelloを呼び出している。