Python

【Python 14】クラス(追補編)

環境

macOS Big Sur
Python3.8

多重継承

複数のスーパークラスから継承することを「多重継承」といいます。

多重継承

class サブクラス名(スーパークラス名1, スーパークラス名2, …):
    クラスの内容

多重継承の場合、サブクラスから参照するメソッドの探索順序を注意する必要があります。

例えば、メソッドaを定義したクラスAをサブクラスB、Cで継承し(便宜上、それぞれB(A)、C(A)と表記します。)、B(A)及びC(A)でAのメソッドaをオーバーライドしたとします。

さらに、B(A)とC(A)を多重継承したサブクラスD(便宜上、D(B, C)と表記します。DではB(A)とC(A)のメソッドをオーバーライドしていないものとします。)を想定します。D(B, C)がaのメソッドを呼び出す際、以下のような探索が行われます。

  1. メソッドの探索順は左のクラスから順番に探索する。
  2. かつスーパークラスの深さ優先で探索する。
  3. ただし、共通のスーパークラスがある場合は最後に1度だけ探索する。

①、②からD→B→A→C→Aとなりますが、③を考慮し、結果的にD→B→C→Aの順番で探索されます。

なお、このルールに反するようなクラス定義をするとエラーになります。(例:D(A, B , C))

また、super()関数においても、この探索順を踏襲します。(D(B, C)のsuper()関数はB→C→Aの順に探索します。)

メソッドの探索順はmro()メソッド」で確認することができます。mroは「Method Resolution Order(メソッド解決順序)」の略です。

class A:
    def __init__(self):
        print('クラスA')


class B(A):
    def __init__(self):
        super().__init__()  # クラスB単体では、クラスAの初期化メソッドを呼び出します。


class C(A):
    def __init__(self):
        print('クラスC')


class D(B, C):
    def __init__(self):
        super().__init__()


print(D.mro())
i = D()


# 実行結果
[<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.A'>, <class 'object'>]
クラスC

Mixin

同名のインスタンス変数を定義している複数のクラスを多重継承することで、異なる扱いを想定していたにも関わらず、インスタンス変数が共有されることで、予期しない問題が引き起こされてしまうことがあります。

この問題を回避するための方法として「Mixin」という考え方があります。

Mixin
  • インスタンス変数を継承するスーパークラスは一つに限定する。
  • その他のスパークラスは、メソッドのみを実装する。