3.18. クラス継承

クラス継承の基礎

Pythonではクラスを継承して、既存クラスを容易に拡張することができます。

3.18.1. クラス継承の基礎

親クラスである「Couクラス継承もオブジェクト指向の基本機能です。親クラスを子クラスが継承することにより親クラスのメソッドを子クラスでも利用できるようになります。このような機能は大規模、複雑なソフトウエアの開発においてメリットがあり、ほぼすべてのプログラミング言語で標準的に備わっています。

3.18.2. クラス継承の利用

男女の知人のデータ処理をするとします。、男女の対応に若干の違いはあるが、知人という点では処理の大部分は共通のものになるでしょう。継承を用いれば、このような場合、共通部分を一つの親クラスで定義し、男女にあわせた処理を共通の処理を継承した子クラスとして定義できます。ここでは男女共通処理をクラスC0、男性の処理をC1、女性の処理をC2として記述してみます。

まずC0とC1の定義と実行例を示します。

クラスC0, C1の定義と実行例

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
    # 住所を記録するスーパークラス
    class C0:
      def seta(self,x):
         self.addr=x
      def geta(self):
         return(self.addr)
      def where(self):
         print("addr:",self.addr)

    # 男性のデータ(住所はC0で処理)
    class C1(C0):
      def __init__(self,x):
         self.name=x
      def show(self):
         print("He is",self.name,"from",self.geta())

    o1=C1("Tom")
    o1.seta("Hill West")
    o1.where()
    o1.show()

2行目はクラスC0の定義で住所の記録と取り出しを処理します。12行目はクラスC1の定義であり、C1(C0)という表記はクラスC1がクラスC0を継承することを表します。クラスC1には住所の機能はありません。メソッド showはオブジェクトの情報を表示しますが、self.geta() はクラス C0のメソッドgeta が参照されます。

実行結果

1
2
3
4
5
6
7
>>> o1=C1("Tom")
>>> o1.seta("Hill West")
>>> o1.where()
addr: Hill West
>>> o1.show()
He is Tom from Hill West
>>>

1行目でオブジェクトo1はクラスC1のインスタンスとして作成されます。2行目のメソッドsetaと2行目のメソッドwhereはクラスC1のメソッドではありません。しかしC1がC0を継承しているので、C0のメソッドで実行することができ、結果として

addr: Hill West

が実行されます。同様にクラスC1のメソッドshowの処理においてメソッド呼び出しself.geta()もクラスC0のメソッド定義によって処理されます。

3.18.3. 親クラスの再利用

次に女性の知人の処理C2で、クラスC0の定義を再利用する例を作ってみます。処理の共通部分にC0を利用することで、C2はをより簡単に実装できます。

C2の定義

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
# 女性のデータ(住所はC0で処理)
class C2(C0):
  def __init__(self,x):
     self.name=x
  def show(self):
     print("She is",self.name,"from",self.geta())

o2=C2("An")
o2.seta("Water Place")
o2.where()
o2.show()

2行目のクラスC2は住所を扱うクラスC0を継承します。C2はshowにおいて、女性用に

She is ...

という表現を使う以外は男性用のクラスC1と同じです。実行結果は以下のようになります。

実行結果

1
2
3
4
5
6
7
>>> o2=C2("An")
>>> o2.seta("Water Place")
>>> o2.where()
addr: Water Place
>>> o2.show()
She is An from Water Place
>>>

この例では、継承のメリットは大きくありませんが、共通処理が増えれば継承のメリットはより大きくなります。

C1やC2は実行時にC0を参照するわけではない、ということに注意してください。先の例のあと以下のようなクラス定義と命令を入力してみましょう。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
class C0:
  def locate(self,x):
     self.addr=x
  def geta(self):
     return("SECRET!!")
  def where(self):
    print("addr: SECRET!")

o1=C1("Tom")
o1.seta("Hill West")
o1.where()
o1.show()

実行結果

1
2
3
4
5
6
7
>>> o3=C1("Tom")
>>> o3.seta("Hill West")
>>> o3.where()
addr: Hill West
>>> o3.show()
He is Tom from Hill West
>>>

C0の定義を変更してから、あらたにC1のインスタンスとしてo3を作成したにもかかわらず、o3.where()やo3.show()を実行すると、変更前のC0の定義に従ってメソッドが処理されています。

Python処理系がC1(C0)という記法を処理した以後、C0の定義が上書きされても、継承されたC0の定義には反映されません。

本来クラスには一つの定義が与えられるべきでしょう。ソースコードやpython実行のセッション内でク二つの定義を与えても必ずしもエラーになりませんが、混乱を避けるためには、クラスの再定義は常に避けるのが賢明です。