Pythonのクラスについて学んでいますが。部品の設計図とも言われるクラスを作ると、なんども関数を定義しなくても、クラスからオブジェクトを作って利用すれば、手間もかからずコードがスッキリもしました。クラスの定義についてはこちらを確認しましょう。
そこで、新たに別のクラスを作った場合、メソッドをまた定義すると場合によっては他のクラスと同じメソッドが必要になったりすることがあります。それをまた一から定義するのはとても非効率です。
そこで、別のクラスのメソッドを他のクラスで使えるとなると便利ですよね。クラスはそれをすることができます。
使いたい別のクラスを指定して、使いたい機能を追加したり書き換えたりすることで新しいクラスを作ることができます。これをクラスの継承と言います。一度作ったクラスは別のクラスで再利用することができる方法とも言えますね。
ここではPytonのクラス継承についてみていきたいと思います。
クラスの継承
新しくクラスを作るとき、すでにある別のクラスを再利用してクラスを継承するとき、この別の元からあるクラスのことを親、スーパークラス、基底クラスと呼びます。新しくつくるクラスのことは、子、サブクラス、派生クラスと呼びます。
クラスの継承の書き方はこのような形になります。
class 子クラス名(親クラス名):
pass # クラス定義
具体的にコードを作ってクラスの継承を見て行きましょう。
次のように親クラスとそれを継承した子クラスのコードを書いてみました。(ホンダの車をコード例に使ってます。特に他意はありません。ただ私がホンダ党なだけです)
class HondaCar(): # 親クラス
def drive(self):
print("車を走らせます。")
def motor_drive(self):
print("モーターで車を走らせます。")
class CivicCar(HondaCar): # 子クラス
pass
honda_car = HondaCar()
honda_car.drive()
civic_car = CivicCar()
civic_car.drive()
親クラスには2つのメソッドを定義しています。selfを入れるのを忘れないようにしましょう。子クラスは丸括弧の中に親クラスを継承するように記入しています。ただし、メソッドは定義せずにpassを置いています。
親クラスをオブジェクト化して、drive()メソッドを実行するコードと、子クラスをオブジェクト化してdrive()メソッドを実行するコードを最後に置いています。
このコードを実行してみましょう。(ファイル名はclass_inheritance.pyにしてAtomで実行しています)
メソッドが実行されていますが、上側(親クラス)が実行されているのは当然ですよね。これまでと一緒です。
下側(子クラス)が実行されているのは不思議ですよね。子クラスにはpassを置いて何も行わなないとしか定義していないにも関わらずメソッドが実行されています。これがクラスを継承したことでできることです。親クラスを継承することで、親クラスに定義されているメソッドを子クラスで利用することができるのです。
このコードに次のようなもう一つ子クラスを作ってみます。
class HondaCar():
def drive(self):
print("車を走らせます。")
def motor_drive(self):
print("モーターで車を走らせます。")
class CivicCar(HondaCar):
pass
class NsxCar(HondaCar):
def track_mode(self):
print("サーキット走行モードで走らせます!")
nsx_car = NsxCar()
nsx_car.drive()
nsx_car.motor_drive()
nsx_car.track_mode()
3つ目のクラスも1つ目のクラスを継承させて、一つだけこのクラスに特有のメソッドを定義しています。
コードを実行してみます。
継承した親クラスのメソッドと、クラス内に定義したメソッドが実行されているのがわかります。
このようにすることで、基本となる機能を一番最初のクラス(親クラス)に定義して、子クラスでその機能を継承していくことができます。そしてここでは3つ目のクラス(子クラス)にはそれ特有の機能を定義して加えていくことができるということになっています。
こうすると、コードがスッキリしているのがわかりますね。これをクラスの継承無しで同じ結果になるように書いてみるとこうなってしまいます。
class HondaCar():
def drive(self):
print("車を走らせます。")
def motor_drive(self):
print("モーターで車を走らせます。")
class CivicCar():
def drive(self):
print("車を走らせます。")
class NsxCar():
def drive(self):
print("車を走らせます。")
def motor_drive(self):
print("モーターで車を走らせます。")
def track_mode(self):
print("サーキット走行モードで走らせます!")
上のコードと比較して見るとわかると思いますが、コードが増えますし、同じメソッドをあちこち定義しないといけないのでとても非効率ですよね。
これがクラス継承との違いになります。
メソッドのオーバーライド
新しいクラスである子クラスはs親クラスの機能を全て継承します。その継承した機能を変更して利用したい場合はメソッドを上書きして使うことができます。
これをメソッドのオーバーライドと言います。
これまで使ってきたコードを利用して見てみましょう。
class HondaCar():
def drive(self):
print("車を走らせます。")
def motor_drive(self):
print("モーターで車を走らせます。")
class CivicCar(HondaCar):
def drive(self): # メソッドのオーバーライド
print("車を燃費良く走らせます。")
class NsxCar(HondaCar):
def drive(self): # メソッドのオーバーライド
print("車をスポーティーに走らせます。")
def track_mode(self):
print("サーキット走行モードで走らせます!")
civic_car = CivicCar()
civic_car.drive()
nsx_car = NsxCar()
nsx_car.drive()
親クラスと同じメソッド名を定義して異なる結果を表示するコードに変えています。ここがオーバーライドした部分です。
このコードを実行して、オーバーライドしたメソッドを呼び出してみましょう。
クラスを継承していますが、親クラスと同じメソッドを呼び出してもオーバーライドすれば親とは違う結果を得ることができるということがわかりますね。
superで親クラスのメソッドを取得
このコードをさらに使って行きます。親クラスに初期化メソッドを定義します。engineというデフォルト引数を与えています。ここではNoneとしています。
こうすると引数を利用して継承先のクラスをインスタンス化してオブジェクトにすれば、クラス変数として利用することができます。
class HondaCar():
def __init__(self, engine=None): # 初期化メソッドでengine名を引数として設定
self.engine = engine
def drive(self):
print("車を走らせます。")
def motor_drive(self):
print("モーターで車を走らせます。")
class CivicCar(HondaCar):
def drive(self):
print("車を燃費良く走らせます。")
class NsxCar(HondaCar):
def drive(self):
print("車をスポーティーに走らせます。")
def track_mode(self):
print("サーキット走行モードで走らせます!")
civic_car = CivicCar("1.5L VTEC TURBO") # engine名を設定
print(civic_car.engine)
nsx_car = NsxCar("3.5L V6 DOHC ツインターボ+3モーター") # engine名を設定
print(nsx_car.engine)
二つの子クラスにエンジン名をそれぞれ代入してオブジェク化しています。それをクラス変数にドット(.)でアクセスしてeigine名を出力しています。
実行結果はこうなります。
これは親クラスの初期化メソッドを継承して利用してるということになります。
ここでCivicCarクラスに初期化メソッドを定義してみます。
class HondaCar():
def __init__(self, engine=None):
self.engine = engine
def drive(self):
print("車を走らせます。")
def motor_drive(self):
print("モーターで車を走らせます。")
class CivicCar(HondaCar):
def __init__(self, engine="1.5L VTEC TURBO", color="黒"): # 初期化メソッド
self.engine = engine
self.color = color
def drive(self):
print("車を燃費良く走らせます。")
class NsxCar(HondaCar):
def drive(self):
print("車をスポーティーに走らせます。")
def track_mode(self):
print("サーキット走行モードで走らせます!")
civic_car = CivicCar()
print(civic_car.engine)
print(civic_car.color)
nsx_car = NsxCar("3.5L V6 DOHC ツインターボ+3モーター")
print(nsx_car.engine)
コードが長くなったので、CivicCarクラスのところ部分だけ抜き出してみます。
class CivicCar(HondaCar):
def __init__(self, engine="1.5L VTEC TURBO", color="黒"): # 初期化メソッド
self.engine = engine
self.color = color
def drive(self):
print("車を燃費良く走らせます。")
初期化メソッドでengineに値を入れて、さらにcolorとして色を定義してみました。engine、colorの値をそれぞれselfで保持しています。
コード全体を実行するとこうなります。
こうなるのはいいのですけども、この初期化によって親クラスの初期化メソッドも全部上書きすることになるので、selfで値を保持するコードを再度書いてしまうことになります。engineの部分は親クラスの初期化と全く同じことを書いていますよね。
これを避ける為に、この部分で親クラスの初期化を呼び出すということができます。これを親クラスの要請と言ったりしますが、superを使って書き換えることができます。
class CivicCar(HondaCar):
def __init__(self, engine="1.5L VTEC TURBO", color="黒"):
super().__init__(engine) #親クラスの要請
self.color = color
def drive(self):
print("車を燃費良く走らせます。")
このクラスの部分だけ書き換えています。
super()で親クラスを示して、ドット(.)で初期化メソッドを呼んで、ここにengineを渡しています。
そして、このクラスだけのクラス変数(ここではcolor)を付け加えているということになっています。
ここではあまりコードに違いがないですが、親クラスの初期化メソッドが、このengineの部分でもっといろんな処理を行なっているコードになっているとしたならば、書き換え前のコードにももっと同じコードを書かなくてはいけないことになってしまいます。
そういう手間をsuperを使った親クラスの要請によって避けることができ、さらにそのクラスでの独自の独自変数を定義することができるわけです。
このsuperによる親クラスへの要請を使った全体のコードをあらためて書いておくとこうなります。
class HondaCar():
def __init__(self, engine=None):
self.engine = engine
def drive(self):
print("車を走らせます。")
def motor_drive(self):
print("モーターで車を走らせます。")
class CivicCar(HondaCar):
def __init__(self, engine="1.5L VTEC TURBO", color="黒"):
super().__init__(engine)
self.color = color
def drive(self):
print("車を燃費良く走らせます。")
class NsxCar(HondaCar):
def drive(self):
print("車をスポーティーに走らせます。")
def track_mode(self):
print("サーキット走行モードで走らせます!") #見やすくするためコードを閉じています。
civic_car = CivicCar()
print(civic_car.engine)
print(civic_car.color)
nsx_car = NsxCar("3.5L V6 DOHC ツインターボ+3モーター")
print(nsx_car.engine)
再度、実行してみましょう。
superを使う前のコードと同じ結果になっています。
このようにsuper()を使って親クラスのメソッドを呼び出すことができます。
まとめ
Pythonのクラスは、別のクラスから全ての機能を利用することができます。これをクラスの継承と言います。
元のクラスを親クラス、スーパークラス、基底クラスと呼び、機能を引き継いで新しく作ったクラスを子クラス、サブクラス、派生クラスと呼びます。
継承することで、子クラスでは定義していなかったメソッドを親クラスの機能を利用して使うことができます。もちろん、子クラス独自のメソッドを合わせて定義することができます。
継承した親クラスのメソッドを子クラスで使う時に、同じメソッド名で定義し直すこともできます。同じメソッド名でも別の動きをさせるように上書きすることができるわけです。これをメソッドのオーバーライドと言います。
子クラスで初期化メソッドを定義する時、親クラスと同じような初期化メソッドの内容を書かなくてはならないことがあります。この手間を避ける為に、superを利用して親クラスの初期化メソッドを呼び出すことで、そこに値を渡すことができます。そして独自のメソッドも定義することができます。