[解決!Python]クラスを継承するには解決!Python

Pythonで単一継承する方法、メソッドをオーバーライドする方法、多重継承する方法、多重継承とMRO、協調的な多重継承について説明する。

» 2023年12月01日 05時00分 公開
[かわさきしんじDeep Insider編集部]

この記事は会員限定です。会員登録(無料)すると全てご覧いただけます。

「解決!Python」のインデックス

連載目次

* 本稿は2022年2月1日に公開された記事をPython 3.12.0で動作確認したものです(確認日:2023年12月1日)。


単一継承

class B:
    def __init__(self, a):
        print('B init')
        self.a = a

    def some_method(self):
        print(f'a: {self.a}')

class D(B):
    def __init__(self, a, b):
        print('D init')
        super().__init__(a)
        self.b = b

    def another_method(self):
        self.some_method()
        print(f'b: {self.b}')


d = D(1, 2)
# 出力結果
#D init
#B init
d.some_method()  # a: 1
d.another_method()
# 出力結果
a: 1
b: 2


 説明は以下の「単一継承」を参照のこと。

メソッドのオーバーライド

class B:
    def __init__(self, a, b):
        self.a = a
        self.b = b

    def some_method(self):
        print(f'a: {self.a}, b: {self.b}')

    def calc_sum(self):
        return self.a + self.b

class D(B):
    def __init__(self, a, b, c, d):
        super().__init__(a, b)
        self.c = c
        self.d = d

    def another_method(self):
        self.some_method()
        print(f'c: {self.c}, d: {self.d}')

    def calc_sum(self):  # overrides B's calc_method
        tmp = super().calc_sum()
        # tmp = B.calc_sum(self)
        return tmp + self.c + self.d


d = D(1, 2, 3, 4)
print(d.calc_sum())  # 10


 説明は以下の「メソッドのオーバーライド」を参照のこと。

多重継承

class B1:
    def __init__(self, a):
        print('B1 init')
        self.a = a

class B2:
    def __init__(self, b):
        print('B2 init')
        self.b = b

class D(B1, B2):
    def __init__(self, a, b, c):
        print('D init')
        B1.__init__(self, a)
        B2.__init__(self, b)
        self.c = c

d = D(0, 1, 2)
print(f'a: {d.a}, b: {d.b}, c: {d.c}'# a: 0, b: 1, c: 2


 説明は以下の「多重継承」「多重継承とMRO」を参照のこと。

協調的な多重継承

class B0:
    def __init__(self):
        print('B0 init')

class B1(B0):
    def __init__(self, a, **kwargs):
        print('B1 init')
        super().__init__(**kwargs)
        self.a = a

class B2(B0):
    def __init__(self, b, **kwargs):
        print('B2 init')
        super().__init__(**kwargs)
        self.b = b

class D(B1, B2):
    def __init__(self, c, **kwargs):
        print('D init')
        super().__init__(**kwargs)
        self.c = c

d = D(a=0, b=1, c=2)
print(f'a: {d.a}, b: {d.b}, c: {d.c}')


 説明は以下の「協調的な多重継承」を参照のこと。

単一継承

 Pythonでクラスを継承する際には、単一継承もしくは多重継承を行える。単一継承では、「class 派生クラス名(規定クラス名):」のようにclass文の先頭でクラス名に続けて継承元のクラスを指定する。

 例えば、以下のようなクラスがあり、そのクラスを継承するクラスを定義したいとする。

class B:
    def __init__(self, a):
        print('B init')
        self.a = a

    def some_method(self):
        print(f'a: {self.a}')


 このクラスBはデータ属性aを持ち、__init__メソッドではこの属性の初期化を行っている(Bは継承元を意味する「Base」を省略したもの)。some_methodメソッドは属性aの値を表示するだけだ。なお、このクラス定義では「class B:」とだけ書いているが、これはPythonに組み込みのobjectクラスを継承することを意味している。「class B(object):」と書いても同様である。

 このクラスを継承するクラスDの定義例を以下に示す(Dは派生を意味する「Derived」を省略したもの)。

class D(B):
    def __init__(self, a, b):
        print('D init')
        super().__init__(a)
        self.b = b

    def another_method(self):
        self.some_method()
        print(f'b: {self.b}')


 クラスDはクラスBを継承するので、class文は「class D(B):」で始まっている。また、__init__メソッドではsuper関数を使ってクラスBを参照するオブジェクトを取得し、それを介してクラスBの__init__メソッドを呼び出している。クラスDの__init__メソッドは2つのパラメーターに値を受け取り、そのうちの1つをクラスBの__init__メソッドに渡すことで、継承元であるクラスBのインスタンスが持つ属性aを初期化している。残りの1つで自身が持つ属性bの初期化も行っている。

 another_methodメソッドはクラスBで定義されているsome_methodメソッドを呼び出して、その属性aの値を表示した後に、派生クラスDのインスタンスが持つ属性bの値を表示している。

 実際にクラスDのインスタンスを生成して、上記2つのメソッドを呼び出すと次のようになる。

d = D(1, 2)
# 出力結果
#D init
#B init
d.some_method()  # a: 1
d.another_method()
# 出力結果
a: 1
b: 2


 「super().__init__(1)」呼び出しにより、クラスBの__init__メソッドが呼び出され、「B init」と出力されていることに注意。

メソッドのオーバーライド

 派生クラスでは基底クラスで定義されているメソッドをオーバーライドできる。オーバーライドするには、基底クラスで定義されているメソッドと同じ名前のメソッドを派生クラスで定義すればよい。

 例えば、次のようなクラスがあり、これを基に派生クラスDを定義したいものとする。

class B:
    def __init__(self, a, b):
        self.a = a
        self.b = b

    def some_method(self):
        print(f'a: {self.a}, b: {self.b}')

    def calc_sum(self):
        return self.a + self.b


 先ほどとほぼ同様だが、今度は基底クラスに2つの属性がある。some_methodメソッドも先ほどと同様に属性の値を出力するだけだ。calc_sumメソッドは2つの属性の和を計算する。

 この派生クラスの定義例を以下に示す。

class D(B):
    def __init__(self, a, b, c, d):
        super().__init__(a, b)
        self.c = c
        self.d = d

    def another_method(self):
        self.some_method()
        print(f'c: {self.c}, d: {self.d}')

    def calc_sum(self):  # overrides B's calc_method
        tmp = super().calc_sum()
        #tmp = B.calc_sum(self)
        return tmp + self.c + self.d


 こちらも先ほどと同様だ。大きな違いはcalc_sumメソッドをこのクラスでも定義している(=calc_sumメソッドをオーバーライドしている)ことだ。このメソッドの中では「super().__calc__sum()」として、クラスBで定義されている同名のメソッドを呼び出している点にも注意しよう。オーバーライドした側から、元のメソッドはこのようにして呼び出せる。あるいは「B.calc_sum(self)」と書いてもよい。

 このクラスのインスタンスを生成して、利用する例を以下に示す。

d = D(1, 2, 3, 4)
print(d.calc_sum())  # 10


多重継承

 多重継承を行うには、class文で基底クラスをカンマ区切りで並べる。例えば、以下のような2つのクラスがあり、それらを継承したクラスを定義したいとする。

class B1:
    def __init__(self, a):
        print('B1 init')
        self.a = a

class B2:
    def __init__(self, b):
        print('B2 init')
        self.b = b


 これらのクラスは基底クラスを特に指定していないので、objectクラスを継承するものだ。objectクラスの__init__メソッドは実質的には何もしないので、__init__メソッドでは「super().__init__()」のようにしてobjectクラスの__init__メソッドを呼び出してはいない。

 クラスB1とクラスB2を基底クラスとするクラスDの定義例を以下に示す。

class D(B1, B2):
    def __init__(self, a, b, c):
        print('D init')
        B1.__init__(self, a)
        B2.__init__(self, b)
        self.c = c


 class文は「class D(B1, B2):」のようにして始めることで、クラスDがクラスB1とB2を継承していることが分かる。__init__メソッドでは両者のインスタンスが持つ属性を初期化するために「B1.__init__(self, a)」「B2.__init__(self, a)」としてそれぞれのクラスの__init__メソッドを呼び出している。

 このクラスのインスタンスを生成して、使用する例を以下に示す。

d = D(0, 1, 2)
print(f'a: {d.a}, b: {d.b}, c: {d.c}'# a: 0, b: 1, c: 2


多重継承とMRO

Copyright© Digital Advantage Corp. All Rights Reserved.

RSSについて

アイティメディアIDについて

メールマガジン登録

@ITのメールマガジンは、 もちろん、すべて無料です。ぜひメールマガジンをご購読ください。