TL;DR
継承とは、クラスを定義する際に基底クラスを指定することで、基底クラスの性質を受け継ぎ、コードの再利用を実現する方法である。
クラスの継承のポイント
クラス名の後に(基底クラス名)
と指定する。
class Car:
pass
class Sedan(Car):
pass
- 基底クラスとは親クラスのこと
- サブクラスとは基底クラスを継承したクラス
- 継承により基底クラスの性質を継承した子クラスを定義できる
- 新しいメソッドと変数を追加できる
- 基底クラスのメソッドを上書きできる
クラスがほかのクラスを継承したかどうかは、issubclass()
を使って判定する。
class Car:
pass
class Sedan(Car):
pass
issubclass(Sedan, Car)
# True
それぞれのクラスからインスタンスを作り、name メソッドを呼び出してみると、基底クラスを継承していることが分かる。
class Car:
def name(self):
print('私は車です。')
class Sedan(Car): # Car を継承
pass
car = Car()
sedan = Sedan()
car.name() # 私は車です。
sedan.name() # 私は車です。
super()
メソッドのオーバーライドとは、基底クラスが持つメソッドと同名のメソッドを定義することによって、そのメソッドを上書することである。
class クラス名(基底クラス):
def __init__(self, 引数):
super().__init__(引数)
処理
オーバーライドしたメソッドでは、基底クラスのメソッドが自動で呼ばれないので、明示的に呼び出す必要がある。基底クラスのメソッドを呼び出すときは、呼び出し先の関数のブロック内に、組み込み関数 super() を記述する。
class Car:
def __init__(self, name):
self.name = name
class Color(Car): # Car を継承
def __init__(self, name, color): # color 引数を追加
super().__init__(name) # 基底クラスのメソッド呼び出し
self.color = color # 基底クラスに含まれていない要素
sedan = Color('CROWN', 'BLACK')
sedan.name # 'CROWN'
sedan.color # 'BLACK'
上に書いたコードが行っているのは、以下である。
- super()が基底クラスの定義を取り出す
super().__init__(name)
メソッドが、Car.__init__(name)
メソッドを呼び出す- 引数 color が、基底クラスに渡され処理される
将来、基底クラスの定義が変更されても、super()を使っていれば、基底クラスから継承している属性やメソッドは、その変更を反映したものになる。
# 基底クラス(継承元)
class Parent:
def __init__(self, a, b):
self.a = a
self.b = b
def w (self):
return self.b
# 継承先
class Child(Parent):
def w(self): # メソッドのオーバーライド
return super().w()
child = Child(0, 'hello')
child.w()
# 'hello'
# 基底クラス(継承元)
class Parent:
def __init__(self, name, age):
self.name = name
self.age = age
def my_name(self):
print("名前は" + self.name + "。年齢は" + str(self.age) + "歳。")
# 継承先
class Child(Parent):
def __init__(self, name, age):
super().__init__(name, age) # オーバーライド
def my_hello(self):
print("こんにちは")
# インスタンス化
yamada = Child("山田", 20)
yamada.my_name()
yamada.my_hello()
# 名前は山田。年齢は20歳。
# こんにちは
多重継承
基底クラスに複数のクラスを指定する。基底クラスをカンマ区切りで並べると多重継承になる。Python の継承は、メソッド解決順序により決まる。
class A:
def __init__(self, name):
self.name = name
def method(self):
print('A')
return self.name
class B:
def __init__(self, name):
self.name = name
def method(self):
print('B')
return self.name
class C(A, B):
pass
c = C('ccc')
c.method()
# A
# ccc
ひし形継承問題
以下のように複数の継承元が同じメソッド名にすると競合が起きる。無謀な継承は避けるべきである。
class A:
def hello(self):
print('hello A')
class B(A):
def hello(self):
print('hello B')
super().hello()
class C(A):
def hello(self):
print('hello C')
super().hello()
class D(B, C):
def hello(self):
print('hello D')
super().hello()
d = D()
d.hello()
# hello D
# hello B
# hello C
# hello A
Mixin
Mixin (ミックスイン)とは、もとのクラス階層とは直接的に関連しない処理をまとめたものである。つまり、クラス定義にヘルパーとして使うだけの親クラスを組み込むことができる。次に示すコードは、Python の標準ライブラリである pprint モジュールをインポートして、pprint.pprint()
メソッドを使い、オブジェクトをきれいに整形して出力する例である。
import pprint
class Mixin:
def feature(self):
pprint.pprint(vars(self))
class Taro(Mixin):
pass
f = Taro()
f.name = 'yamada'
f.age = 17
f.broad = 'B'
f.feature()
# {'age': 17, 'broad': 'B', 'name': 'yamada'}