情報系大学院生の勉強メモ

主に機械学習,Python

Pythonのメソッド解決順序(MRO)

MROとは

MROとは、Method Resolution Orderの略で、多重継承したクラスのメソッドが呼び出されるときに,どのメソッドがどういう順番で呼び出されるかのことです。Pythonはv2.3以降C3線形化アルゴリズムというアルゴリズムで順序を特定しています。

C3線形化アルゴリズム

簡単にいうと

  • クラスAがBを継承しているなら必ずAがBより先にくる

  • AがB,Cの順番で継承しているならBがCより前にくる

ように並べるアルゴリズムです。

公式ドキュメントがより詳しいです↓

The Python 2.3 Method Resolution Order | Python.org

具体例

早速、具体例を見ていきます。

O = object
class F(O): pass
class E(O): pass
class D(O): pass
class C(D,F): pass
class B(D,E): pass
class A(B,C): pass

print(A.mro())

とすると

[<class '__main__.A'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.D'>, <class '__main__.E'>, <class '__main__.F'>, <class 'object'>]

と出力されます。 つまりMROは「A→B→C→D→E→F→O」です

まず

class A(B,C): pass

よりA→B→Cの順序が守られなくてはなりません。

なぜならクラスAがクラスBとCを継承しているのでAはB,Cのどちらよりも前に来ないといけなくて、またB,Cの順で継承しているのでBはCより前に来ないといけないからです。

同様に

  • B→D→E
  • C→D→F
  • D→O
  • E→O
  • F→O の順番が守られなくてはいけません。

「A→B→C→D→E→F→O」より確かにこれらの順番が守られていることがわかります。

もちろんうまく順序付けられない場合もあります。たとえば以下の例の場合があります。

class A: pass
class B(A): pass
class C(A,B): pass
print(C.mro())

これを実行すると

TypeError: Cannot create a consistent method resolution
order (MRO) for bases A, B

と怒られます。

なぜなら、まずCはA,Bの順番で継承しているので、A→Bの順番が守られなくてはなりません。一方BはAを継承しているのでB→Aの順番が守られなくてはなりません。ここで矛盾が起きています。よってエラーが発生します。

MROを知らないと時間を溶かしそうなエラーですね...

最後にメソッドを付け加えて見てみましょう。

class A:
    def process(self):
        print('Aのprocess')

class B(A):
    pass

class C(A):
    def process(self):
        print('Cのprocess')

class D(B,C):
    pass

obj = D()
obj.process()

継承関係を図にすると、以下のようになります。

diagram

さて、obj.process()によって何が出力されるでしょうか?

これはMROが分かれば簡単で、ここでMROはD→B→C→Aです。まずDにはprocess関数が定義されていないので次にBを見ます。Bにもprocess関数は定義されていないので次にCを見ます。Cには定義されているので、出力は

Cのprocess

になります。これだけです。またこの時Aのprocessは呼ばれません。

まとめ

  • MROはどの順番でメソッドが呼び出されるかのこと。
  • PythonMROにはC3線形化アルゴリズムが用いられている。
  • 継承関係によってはうまく順序づけられない場合もある。

参考