クラスの階層や多重継承について

10.5 クラスの階層や多重継承について

本章の学習でクラスを拡張する様々な仕組みについてみてきました。クラスの拡張と継承についての関係をさらに詳しくみていきましょう。

10.5.1 クラスの階層について

これまでのプログラム中で普通に行ってきましたが、複数のサブクラスに1つの同じスーパークラスを継承することもできます。この時のクラス同士の関係は以下の図1.5.1のようになります。

図 10.5.1: 複数のサブクラスで1つのスーパークラスを継承

また、サブクラスを継承したさらに新しいサブクラスを作成することもできます。新しいサブクラスから見ると最初のサブクラスがスーパークラスになります。新しいサブクラスには、サブクラスのメンバとして大元のスーパークラスのメンバも継承されることになります。関係は以下の図1.5.2になります。

図 10.5.2: サブクラスを拡張

10.5.2 多重継承について

また複数のサブクラスに1つの同じスーパークラスを継承することはできますが、Pythonでは1つのサブクラスに複数のスーパークラスを継承することもできるようになっています。

図 10.5.3: 多重継承のイメージ

多重継承の書式

継承はサブクラスを定義する際に引数にスーパークラスの名前を指定することで実現できましたが、多重継承の場合は、引数に複数のスーパークラスの名前を指定することで実現できます。

書式:多重継承

1class サブクラス(スーパークラス①,スーパークラス②,…)

凡例

1class Computer1():
2    def hello(self):
3        print('hello')
4 
5class Computer2():
6    def bye(self):
7        print('bye')
8 
9class notePc(Computer1,Computer2):
10 
11    def greeting(self):
12        super().hello()
13        super().bye()
14 
15npc = notePc()
16npc.greeting()

実行結果

こちらの凡例では、notePcクラスをサブクラスとして、スーパークラスのComputer1、Computer2を継承しております。
notePcクラスでは、Computer1クラスのhello()メソッド、Computer2のbye()メソッドを呼び出すgreeting()メソッドを定義しており、notePcから生成されたインスタンスnpcを介して、greeting()メソッドを呼び出してます。
実行結果からhello()メソッド、bye()メソッドが呼び出せており、二つのクラスを継承できていることがわかります。

10.5.3 多重継承におけるオーバーライドを確認するプログラム

多重継承や継承によるクラスの階層について確認してきましたが、多重継承やクラスの階層において、継承元のスーパークラス間で同じ名前のメソッドが含まれていた場合、どのスーパークラスのメソッドが優先的に実行されるのでしょうか?
以下で多重継承、2階層に渡るクラスの継承においてスーパークラス間のメソッド名が同じプログラムを確認していきましょう。

ソースコード

ソース・フォルダー: /Desktop/Python基礎講座
ファイル名: 第10章.ipynb
アクセスURL: http://localhost:8888/notebooks/Desktop/Python基礎講座/第10章.ipynb

1class Computer1():
2 
3    def name(self):
4        print('Computer1')
5 
6class Computer2():
7 
8    def name(self):
9        print('Computer2')
10 
11class Computer3(Computer1):
12 
13    def name(self):
14        print('Computer3')
15 
16class notePc1(Computer1,Computer2):
17 
18    def name(self):
19        super().name()
20 
21class notePc2(Computer3):
22 
23    def name(self):
24        super().name()
25 
26npc1 = notePc1()
27npc1.name()
28npc2 = notePc2()
29npc2.name()

実行結果

解説

16~19行目でComputer1クラス、Computer2クラスを継承したサブクラスnotePc1を定義しております。notePc1では、スーパークラスのname()メソッドを呼び出す、name()メソッドを定義してます。

1class notePc1(Computer1,Computer2):
2 
3    def name(self):
4        super().name()

26、27行目でnotePc1クラスを介してインスタンスを生成し、こちらのインスタンスからname()メソッドを呼び出してます。

実行結果(「Computer1」と出力)から、Computer2クラスよりComputer1クラスのname()メソッドが優先して呼び出されているのがわかります。
つまりサブクラスを定義する際に、先に指定したスーパークラスのメソッドが優先的に呼び出されるということです。

また11~14行目ではスーパークラスComputer1を継承する、サブクラスComputer3を定義しております。

1class Computer3(Computer1):
2 
3    def name(self):
4        print('Computer3')

さらに21~24行目では、Computer3をスーパークラスとして継承しているサブクラスnotePc2を定義しております。

1class notePc2(Computer3):
2 
3    def name(self):
4        super().name()

notePc2は、実質Computer1とComputer3の両方のクラスを継承していることになりますが、どちらのクラスもname()メソッドが含まれております。
そこで28、29行目ではnotePc2クラスから、インスタンスnpc2を生成し、npc2を介してname()メソッドを呼び出してます。

notePc2のname()メソッドはスーパークラスのname()メソッドを呼び出す処理を行いますが、実行結果からComputer1クラスではなくComputer3クラスのメソッドが優先的に呼び出されることがわかります。
これはComputer3クラスがComputer1クラスを継承した際に、すでにname()メソッドがオーバーライドされているからです。
21~24行目のイメージを図にまとめると以下の通りです。

図 10.5.4: 階層関係におけるスーパークラス間のオーバーライドについて


NEXT>> 10.6 抽象クラスについて

f