【Python】継承の使い方【オーバーライド・super・多重継承】

Python

この記事では、Pythonの継承について解説します。継承はオブジェクト指向型言語のキモとなっている機能なので、しっかり覚えていつでも使えるようにしておきましょう!

継承とは?

継承とは、クラスから派生したクラスを作成することです。派生したクラスを作成することで元のクラスの機能を引き継ぎ、さらに拡張することができます。

またクラスを継承させることで、そのクラスが持つ属性が定義されていることを保証することができます。

継承してみる

コンストラクタのみ定義されているクラスを作成して継承してみます。

class Parent:
    def __init__():
        self.value = 'Parent'

この「Parentクラス」を派生させて「Childクラス」を作成します。

class Child(Parent):
    pass


c = Child()
print(c.value)
# Parent

「Childクラス」では何も定義していませんが「Childクラス」のインスタンスから「Parentクラス」で定義されている「value」を呼び出すことができました。

このように、クラスを継承させることで親の属性を子に引き継がせることができます。

継承元・継承先のクラスの呼び名

クラスの継承元・継承先は以下のように呼びます。

継承元のクラス(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クラスのコンストラクタが呼び出されました
わんちゃん

「Catクラス」と「Dogクラス」には、コンストラクタを定義していないため、初期化には「Animalクラス」のコンストラクタが呼び出されています。

特定のクラスを継承しているか判別する

特定のクラスを継承しているか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クラスを継承していません

このように、特定の親クラスを継承しているかどうかを判別することで、そのクラスが親クラスで定義されている変数やメソッドを呼び出せるかを知ることができます。

オーバーライド

オーバーライドとは、親クラスに定義されているメソッドと同じ名前のメソッドを子クラスで定義することを言います。

オーバーライドすることで、親クラスのメソッドを拡張・変更することができます。

書式

親クラスで定義されているメソッドと同名のメソッドを子クラスで定義します。

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クラス」になっているのがわかる。

まとめ

この記事では、Pythonの継承について解説しました。

継承はオブジェクト指向型言語のキモとなっている機能です。しっかり覚えていつでも使えるようにしておきましょう!

今回のこれだけは覚えておこう

継承からのオーバーライドは覚えておきましょう。

def 親:
    def メソッド(self):
        print('親メソッド')

def 子(親):
    def メソッド(self):
        print('子メソッド')

それでは今回の内容はここまでです。ではまたどこかで〜( ・∀・)ノ

タイトルとURLをコピーしました