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()
継承関係を図にすると、以下のようになります。
さて、obj.process()
によって何が出力されるでしょうか?
これはMROが分かれば簡単で、ここでMROはD→B→C→Aです。まずDにはprocess関数が定義されていないので次にBを見ます。Bにもprocess関数は定義されていないので次にCを見ます。Cには定義されているので、出力は
Cのprocess
になります。これだけです。またこの時Aのprocessは呼ばれません。