この記事では、Pythonの継承の使い方について解説します。継承を使うことで親クラスの性質を引継ぎ、拡張・変更することができます。
それでは、Pythonの継承の使い方を見ていきましょう!
継承とは?
継承とは親クラスから派生した子クラスを定義することです。子クラスを定義することで親クラスの機能を引き継ぎ、さらに拡張することができます。
また、クラスを継承させることでそのクラスが持つ属性が定義されていることを保証することができます。
継承元・継承先のクラスの呼び名
クラスの継承元・継承先は以下のように呼びます。
継承元のクラス(Perentクラス)
基底クラス・親クラス・スーパークラス
継承先のクラス(Childクラス)
派生クラス・子クラス・サブクラス
継承の使い方
継承は以下のように記述します。
class 子クラス(親クラス):
# なんらかの処理
さらに、1つのクラスに複数のクラスを継承させることもできます。
class 子クラス(親クラス1, 親クラス2, ... ,親クラスN):
# なんらかの処理
サンプル
以下のコードは「Animalクラス」を派生させて2つのクラスを作成しています。
class Animal:
def __init__(self, name):
print('Animalクラスのコンストラクタが呼び出されました')
self.name = name
class Cat(Animal):
pass
class Dog(Animal):
pass
neko = Cat('猫ちゃん')
print(neko.name)
inu = Dog('わんちゃん')
print(inu.name)
実行結果
Animalクラスのコンストラクタが呼び出されました
猫ちゃん
Animalクラスのコンストラクタが呼び出されました
わんちゃん
子クラスではコンストラクタを定義していないため、初期化には「Animalクラス」のコンストラクタが呼び出されました!
オーバーライド
オーバーライドとは親クラスに定義されているメソッドと同じ名前のメソッドを子クラスで定義することを言います。
オーバーライドすることで親クラスのメソッドを拡張・変更することができます。
書式
親クラスで定義されているメソッドと同名のメソッドを子クラスで定義します。
class 親:
def メソッド(self):
print('親のメソッド')
class 子(親):
# オーバーライド
def メソッド(self):
print('子のメソッド')
サンプル
以下のコードでは、「Animalクラス」で定義したメソッドを子クラスの「Dogクラス」でオーバーライドしています。「Catクラス」では何もしていません。
class Animal:
# 親クラスのメソッド
def cry(self):
print('Animalのcryメソッド')
# オーバーライドしないクラス
class Cat(Animal):
pass
# オーバーライドするクラス
class Dog(Animal):
def cry(self):
print('ワンワン!')
neko = Cat()
neko.cry()
inu = Dog()
inu.cry()
実行結果
Animalのcryメソッド
ワンワン!
このようにオーバーライドすることで子クラスのメソッドを優先して呼び出すことができます。
親クラスを参照する[super()]
super()を使うことで子クラスから親クラスを参照することができます。
使い方
子クラスのメソッド内からsuper()を使うことで、親クラスにアクセスすることができます。コンストラクタやメソッドにアクセスすることもできます。
class 子(親):
def メソッド(self):
# 親にアクセス
super()
サンプル
以下のコードでは、子クラスのコンストラクタから親クラスのコンストラクタを呼び出しています。
class 親:
def __init__(self, value):
print('親 コンストラクタ start')
self.value = value
print('親 コンストラクタ end')
class 子(親):
def __init__(self, value, data):
print('子 コンストラクタ start')
super().__init__(value) # 親クラスのコンストラクタ呼び出し
self.data = data
print('子 コンストラクタ end')
child = 子('value', 'data')
print(child.value)
print(child.data)
実行結果
子 コンストラクタ start
親 コンストラクタ start
親 コンストラクタ end
子 コンストラクタ end
value
data
super()を使って親クラスのコンストラクタを呼び出すことで、親クラスのコンストラクタを拡張することができます。
メソッドにも同様のことができます。
class 親:
def メソッド(self):
print('親 メソッド')
class 子(親):
def メソッド(self):
super().メソッド()
print('子 メソッド')
child = 子()
child.メソッド()
実行結果
親 メソッド
子 メソッド
継承の継承
継承しているクラスをさらに継承することができます。
以下のコードは「D > C > B > Aクラス」に渡って継承しています。
class A:
def __init__(self, a):
print('A init')
self.a = a
def method(self):
print('A method')
print(f'a: {self.a}')
class B(A):
def __init__(self, a, b):
print('B init')
super().__init__(a)
self.b = b
def method(self):
print('B method')
print(f'a: {self.a}, b: {self.b}')
class C(B):
def __init__(self, a, b, c):
print('C init')
super().__init__(a, b)
self.c = c
def method(self):
print('C method')
print(f'a: {self.a}, b: {self.b}, c: {self.c}')
class D(C):
def __init__(self, a, b, c, d):
print('D init')
super().__init__(a, b, c)
self.d = d
def method(self):
print('D method')
print(f'a: {self.a}, b: {self.b}, c: {self.c}, d: {self.d}')
print('Aのインスタンス化')
a = A(1)
a.method()
print('Bのインスタンス化')
b = B(1, 2)
b.method()
print('Cのインスタンス化')
c = C(1, 2, 3)
c.method()
print('Dのインスタンス化')
d = D(1, 2, 3, 4)
d.method()
実行結果
Aのインスタンス化
A init
A method
a: 1
Bのインスタンス化
B init
A init
B method
a: 1, b: 2
Cのインスタンス化
C init
B init
A init
C method
a: 1, b: 2, c: 3
Dのインスタンス化
D init
C init
B init
A init
D method
a: 1, b: 2, c: 3, d: 4
おじいちゃんを参照
先ほどのコードではsuper()で親クラスのコンストラクタを呼び出していましたが、さらに上の世代のクラスを参照することもできます。
例えば「Dクラス」から「Bクラス」や「Aクラス」を参照することができます。
サンプル
以下のコードでは「Dクラス」のコンストラクタ内で「Bクラス」を参照して「methodメソッド」で「Aクラス」を参照しています。
class D(C):
def __init__(self, a, b, d):
print('D init')
# Bクラスのコンストラクタの呼び出し
super(C, self).__init__(a, b)
self.d = d
def method(self):
# Aクラスのmethodの呼び出し
super(B, self).method()
print('Dのインスタンス化')
d = D(1, 2, 4)
d.method()
実行結果
Dのインスタンス化
D init
B init
A init
a: 1
継承するとメソッド順序解決でメソッドが呼び出される優先順位が決められます。
今回の場合は単純な直列の継承なので子が優先され「D > C > B > Aクラス」という順序になります。そしてsuper関数の第一引数に型を渡すことで、その型の直後から検索されるようになります。
コードを見てみるとコンストラクタのsuper関数では「Cクラス」が渡されているので、「B > Aクラス」という順序で検索されます。
「methodメソッド」内では「Bクラス」が渡されているので、「Aクラス」のみが検索されています。
多重継承
1つのクラスに複数のクラスを継承させることができます。
以下のコードは「A・Bクラス」を「Cクラス」に継承しています。
class A:
def __init__(self, a):
print('A init')
self.a = a
def method(self):
print(f'a: {self.a}')
class B:
def __init__(self, b):
print('B init')
self.b = b
def method(self):
print(f'b: {self.b}')
class C(A, B):
def __init__(self, a, b, c):
print('C init')
super().__init__(a)
self.c = c
def method(self):
print(f'a: {self.a}, c: {self.c}')
c = C(1, 2, 3)
c.method()
実行結果
C init
A init
a: 1, c: 3
このコードではsuper()で「Aクラス」が参照されています。
もう片方のクラスを参照する
先ほどのコードでは「Aクラス」しか参照していなかったので「Bクラス」も参照してみます。
class C(A, B):
def __init__(self, a, b, c):
print('C init')
super().__init__(a)
# Bクラスの参照
super(A, self).__init__(b)
self.c = c
def method(self):
print(f'a: {self.a}, b: {self.b}, c: {self.c}')
c = C(1, 2, 3)
c.method()
実行結果
C init
A init
B init
a: 1, b: 2, c: 3
このようにsuper関数の第一引数に「Aクラス」を指定することで「Bクラス」を参照することができます。
順序を変更する
継承させる順番を変えることで検索順序を変更できます。
左側に記述するほど優先されます。
# 継承させる順番をB > Aに変更
class C(B, A):
def __init__(self, a, b, c):
print('C init')
# Bクラスが参照されている
super().__init__(a)
# Aクラスが参照されている
super(B, self).__init__(b)
self.c = c
def method(self):
print(f'a: {self.a}, b: {self.b}, c: {self.c}')
c = C(1, 2, 3)
c.method()
実行結果
C init
B init
A init
a: 2, b: 1, c: 3
「C > B > Aクラス」の順序で呼び出されるようになりました。
オーバーライドしていない場合
親クラスのメソッドをオーバーライドしていない場合、検索順序には親クラスのメソッドが評価されます。
class Zero:
def method(self):
print('Zero')
class A(Zero):
pass
class B(Zero):
pass
class C(Zero):
def method(self):
print('C')
class Tail(A, B, C):
pass
t = Tail()
t.method()
実行結果
C
検索順序的には「Cクラス」よりも「A・Bクラス」の方が早いですが、メソッドをオーバーライドしてない為、「A・Bクラス」は「Zeroクラス」のメソッドを参照しています。そのため、検索順序は「C > Zero」になっています。
優先順序を取得する [mroメソッド]
優先順位は確認することができます。
書式
mroメソッドを使うことでメソッド順序解決リストを取得できます。
クラス.mro()
サンプル
検索順序を確認してみましょう!
class Zero:
pass
class A(Zero):
pass
class B(Zero):
pass
class Tail(A, B):
pass
print(Tail.mro())
実行結果
[<class '__main__.tail'="">, <class '__main__.a'="">, <class '__main__.b'="">, <class '__main__.zero'="">, <class 'object'="">]
「Tail > A > B > Zeroクラス」になっているのがわかる。
特定のクラスを継承しているか判別する
特定のクラスを継承しているかisinstance()で判別することができます。
書式
第一引数に判別したいオブジェクト、第二引数に判別したい型を指定します。
isinstance(object, classinfo)
object引数がclassinfo引数のインスタンスか、またはサブクラスのインスタンスの場合にTrueが返されます。
サンプル
以下のコードは、Parentクラスを継承しているか判定して継承している場合はParentクラスの属性を呼び出しています。
class Parent:
def __init__(self):
self.value = 'parent'
# Parentを継承したクラス
class Child(Parent):
pass
# 継承していないクラス
class NoParent:
pass
# インスタンス化
c = Child()
n = NoParent()
def func(obj):
# クラス名の出力
print(obj.__class__)
if isinstance(obj, Parent):
print('Parentクラスを継承しています')
print(f'value: {obj.value}') # Parentが継承されていれば必ず定義されている
else:
print('Parentクラスを継承していません')
func(c)
func(n)
実行結果
<class '__main__.Child'>
Parentクラスを継承しています
value: parent
<class '__main__.NoParent'>
Parentクラスを継承していません
このように特定の親クラスを継承しているかどうかを判別することで、そのクラスが親クラスで定義されている属性を呼び出せるかどうかを判別できます。
まとめ
この記事では、Pythonの継承について解説しました。
継承からのオーバーライドは覚えておきましょう。
def 親:
def メソッド名(self):
print('親メソッド')
def 子(親):
def メソッド名(self):
print('子メソッド')
それでは今回の内容はここまでです。ではまたどこかで〜( ・∀・)ノ