クラスの継承-その2
記事の内容
この記事では、Pythonのクラス継承において、super関数を使って親クラスのメソッドを上書きする方法について説明します。
前の記事では、親クラスのプロパティとメソッドを子クラスに継承する最もシンプルなクラスの継承方法について説明しました。Pythonのクラスの継承について初めての方は前の記事を先に読むことをお勧めします。
***わからない用語があるときは索引ページへ***
この記事のサンプルプログラムは、Python3系をベースとしています。Python2系は、クラス宣言の記述ルールが異なるのでご注意ください。
1.子クラスで親クラスのメソッドを上書きする
Pythonのクラス継承において、子クラスで親クラスと同じメソッド名を作成すると、子クラスで作成したメソッドが親クラスのメソッドを上書きします。サンプルプログラムで動きを見てみましょう。
##class宣言
##親クラスKagensanクラスを作成
class Kagensan:
##インスタンスコンストラクタの作成
def __init__(self, a, b):
self.x = a
self.y = b
##インスタンスメソッドの作成
def tashizan(self):
wa = self.x + self.y
return wa
def hikizan(self):
sa = self.x - self.y
return sa
##親クラスを継承して子クラスKagensan2クラスを作成
class Kagensan2(Kagensan):
##親クラスのhikizanメソッドを子クラスで上書き
##親クラスのhikizanメソッドの引かれる数と引く数を入れ替えた。
def hikizan(self):
sa = self.y - self.x
return sa
##プログラム実行開始位置
a = 5
b = 4
print("a =", a, "b =", b, "\n")
##インスタンスの生成
ins1 = Kagensan(a, b)
ins2 = Kagensan2(a, b)
##メソッドの実行
print("子:a + b =", ins2.tashizan(), "\n")
print("親:a - b =", ins1.hikizan())
print("子:b - a =", ins2.hikizan())
このサンプルプログラムは、クラス継承した後で、親クラスのhikizanメソッドを、子クラスのhikizanメソッドで上書きしています。
tashizanメソッドについては、親クラスから継承したものをそのまま使えますが、hikizanメソッドについては子クラスで上書きしているので、子クラスのhikizanメソッドは親クラスのhikizanメソッドと違う動きをしています。
子クラスのhikizanメソッドでは、引かれる数と引く数を入れ替えました。
実行結果
a = 5 b = 4
子:a + b = 9
親:a - b = 1
子:b - a = -1
このように、子クラスから親クラスと同じメソッド名を作成すると、子クラスでは、親クラスから継承したメソッドを上書きします。
2.子クラスから親クラスにアクセスする
Pythonのクラス継承において、子クラスから親クラスのメソッドを実行したくなることがあります。
例えば、子クラスで追加するメソッドのために、親クラスにはないインスタンス変数をコンストラクタで定義するケースを考えてみましょう。
親クラスのコンストラクタにインスタンス変数を1つ追加するだけで良いのですが、子クラスで__init__メソッドを定義すると、親クラスの__init__を上書きしてしまいます。
このような場合、super関数を使って親クラスのメソッドにアクセスすると簡単に親クラスの__init__を拡張することができます。class宣言のプログラムブロックにおけるsuper関数は特別な関数で、親クラスのインスタンスもどきを返す関数です。
補足
苦しい説明でスミマセン。。
子クラスから親クラスのメソッドを呼び出す際、super関数の戻り値は、親クラスのインスタンスのようにふるまいますが、親クラスのインスタンスではありません。ここは注意が必要で、super関数を使って親クラスのメソッドを呼び出すことはできますが、親クラスのインスタンス変数にアクセスすることはできません。
class定義の中で、print(type(super()))を出力してみると、class 'super'と出力されます。super関数の戻り値はsuper型のオブジェクトということですね。
サンプルプログラムで動きをみてみましょう。
##class_keisho_ex3.py
##class宣言
class Kagensan:
##インスタンスコンストラクタの作成
def __init__(self, a, b):
self.x = a
self.y = b
##インスタンスメソッドの作成
def tashizan(self):
wa = self.x + self.y
return wa
def hikizan(self):
sa = self.x - self.y
return sa
class nBaiKagensan(Kagensan):
##子クラスでコンストラクタを改めて定義
def __init__(self, a, b, n=1):
##super関数を使って、
##この行は親クラスのコンストラクタを実行している
super().__init__(a, b)
##インスタンス変数nを子クラスで定義
self.n = n
##子クラスのメソッドを追加
def n_bai_tashizan(self):
n_bai_wa = self.n * self.tashizan()
return n_bai_wa
def n_bai_hikizan(self):
n_bai_sa = self.n * self.hikizan()
return n_bai_sa
##プログラム実行開始位置
a = 5
b = 4
n = 2
print("a=", a, "b=", b, "n=", n, "\n")
##インスタンスの生成
ins1 = nBaiKagensan(a, b, n=n)
##メソッドの実行
print("a + b =", ins1.tashizan())
print("a - b =", ins1.hikizan())
print("\n")
print("n(a+b) =", ins1.n_bai_tashizan())
print("n(a-b) =", ins1.n_bai_hikizan())
実行結果
a= 5 b= 4 n= 2
a + b = 9
a - b = 1
n(a+b) = 18
n(a-b) = 2
3.super関数の解説
class_keisho_ex3.pyの子クラスのコンストラクタを見てみましょう。
##子クラスでコンストラクタを改めて定義
def __init__(self, a, b, n=1):
##super関数を使って、
##この行は親クラスのコンストラクタを実行している
super().__init__(a, b)
##インスタンス変数nを子クラスで定義
self.n = n
super関数の戻り値super()は、親クラスのメソッドを呼び出すことができますので、
super().__init__(a, b)
は、親クラスの__init__メソッド(コンストラクタ)を呼び出して引数にaとbを渡して実行しています。
これで、親クラスのコンストラクタからself.xとself.yに引数aとbがセットされました。
ここをsuper()の代わりに、selfと書いてしまうと、子クラスで上書きされた自分自身のコンストラクタを呼び出してしまいます。
試しにやってみたところ、無限に自分自身を呼び出すことになりますので、Pythonの関数ネストの制限を超えて例外が発生しました。
子クラスで追加したメソッドも見てみましょう。
##子クラスのメソッドを追加
def n_bai_tashizan(self):
n_bai_wa = self.n * self.tashizan()
return n_bai_wa
def n_bai_hikizan(self):
n_bai_sa = self.n * self.hikizan()
return n_bai_sa
親クラスのtashizanメソッド、hikizanメソッドは、親クラスから継承され、子クラスでは書き換えていません。したがって、そのまま自身のインスタンスであるselfを使って、親クラスのtashizanメソッド、hikizanメソッドを呼び出しています。
親クラスのメソッドは、super関数を使って、
##子クラスのメソッドを追加
def n_bai_tashizan(self):
n_bai_wa = self.n * super().tashizan()
return n_bai_wa
def n_bai_hikizan(self):
n_bai_sa = self.n * super().hikizan()
return n_bai_sa
と書いても同じ結果になります。
4.super関数のまとめ
クラスの継承において、super関数を呼び出すと、親クラスのメソッドを呼び出すことができます。
親クラスにあるメソッドを子クラスで上書きした場合、子クラスからselfでアクセスをすると、子クラスで上書きしたメソッドを呼び出します。
したがって、super関数は、明示的に親クラスのメソッドを呼び出すために、
super().<親クラスのメソッド名>(引数, ・・・)
の形式で利用します。super関数で親クラスのメソッドを呼び出した場合は、子クラスで上書きされていたとしても、親クラスのメソッドが呼び出されます。
クラスを継承すると、親クラスのすべてのプロパティ、メソッドはすべて子クラスに引き継がれます。子クラスで書き換えない親クラスのメソッドについては、selfインスタンスからも、super関数を使った親クラスのメソッドからもアクセスすることができます。
5.クラスの継承はいくらでも多段できる
クラスの継承はいくらでも多段に継承できます。親クラスから子クラスを作成し、子クラスから孫クラスを作成する、といった感じです。
祖父母クラスのメソッドを親クラスで上書きした場合、孫クラスからsuper関数で上書きされた親クラスのメソッドを呼び出すと、祖父母クラスのメソッドを上書きした親クラスのメソッドを実行します。
また、祖父母クラスのメソッドはすべて親メソッドに継承されるため、親クラスで祖父母クラスのメソッドを書き換えていない場合は、super関数で祖父母クラスのメソッドを呼び出すこともできます。ただし、これは実際には祖父母クラスのメソッドを呼び出しているのではなく、祖父母クラスから継承した親クラスのメソッドを呼び出しているのです。
この記事が気に入ったらサポートをしてみませんか?