CodeGym /コース /Python SELF JA /メソッド解決順序 (MRO)

メソッド解決順序 (MRO)

Python SELF JA
レベル 16 , レッスン 5
使用可能

11.1 メソッド解決順序

メソッド解決順序 (Method Resolution Order, MRO) は、Pythonがクラスの階層内でメソッドや属性を探す順序を決定するものだよ。 これは特に、複数の親クラスから属性やメソッドを継承する場合に重要だね。

簡単に言うと、Pythonがクラスの継承ツリーを巡るための厳密な固定順序(というよりアルゴリズム)があるんだ。そのアルゴリズムは、メソッドの検索順序を正しく保証するよ。こんな感じで説明できる:

C3線形化アルゴリズム

C3線形化アルゴリズムは、MROを次のように組み合わせることで決定する:

  • クラス自体。
  • 親クラスのリスト(記述されている順序)。
  • 親クラスのMRO(同じ順序)。

C3線形化アルゴリズムのルール

  • ローカルメソッドの順序を維持: クラスAがクラスBよりも前に指定されている場合、クラスAのすべてのメソッドはクラスBのメソッドよりも先に考慮されるべきだよ。
  • 親クラスの順序を維持: クラスAがクラスBの親である場合、クラスAのすべてのメソッドはクラスBのメソッドよりも先に考慮されるべきだよ。
  • 継承順を考慮: クラスCが複数のクラスの親である場合、クラスCのメソッドの順序はこれらすべてのクラスのMROで維持されるべきだよ。

アルゴリズムのステップ:

ステップ1. クラス自体から始める:

常にそのメソッドが呼ばれたクラス自体から始めるんだ。

ステップ2. 基本クラスを記述された順序で追加:

現在のクラスの後で、継承時に指定された順序で基本クラスをチェックするんだ。

ステップ3. 親クラスを巡る:

その中でフィールドとメソッドを探すんだ。

ステップ4. 親クラスのMROを統合する:

同じ基本クラスが複数の経路で継承される場合、それは一度だけ正しい順序でチェックされる(他のすべての回はスキップされる)。

「アルゴリズムとデータ構造」のトピックに既に詳しい人には、これは幅優先探索ではなく、深さ優先探索だとわかるだろうね。

11.2 MROの検証

Pythonでは、__mro__属性やmro()関数を使って、クラスのメソッドやフィールドの巡回順序を確認できるよ。

例:


class A:
    def method(self):
        print("A")
        
class B(A):
    def method(self):
        print("B")
        
class C(A):
    def method(self):
        print("C")
        
class D(B, C):
    def method(self):
        print("D")
        

# MROの検証
print(D.__mro__)
        

出力は次のようになるよ:


(<class '__main__.D'>, 
<class '__main__.B'>, 
<class '__main__.C'>,
<class '__main__.A'>,
<class 'object'>)
        

これは、Pythonがメソッドや属性をどの順序で探すかを示しているよ:

  • D: まずPythonはクラスDでメソッドを探すよ。
  • B: 次にPythonはクラスB(最初の親クラス)でメソッドを探すよ。
  • C: クラスBでメソッドが見つからない場合、PythonはクラスC(2番目の親クラス)でメソッドを探すよ。
  • A: クラスBCでメソッドが見つからない場合、PythonはクラスAでメソッドを探すよ。
  • object: 最終的に、Pythonは基本クラスobjectでメソッドを探すよ。

11.3 super() を使用したMRO

関数 super() は、親クラスのメソッドを正しい順序で呼び出すためにMROに従うんだ。 super()の使用例を見てみよう:

class A:
    def method(self):
        print("A")
        super().method()
        
class B(A):
    def method(self):
        print("B")
        super().method()
        
class C(A):
    def method(self):
        print("C")
        super().method()
        
class D(B, C):
    def method(self):
        print("D")
        super().method()
        
        
d = D()
d.method()
        

出力は次のようになるよ:


D
B
C
A
        

巡回順序 (MRO)

1. クラスDのメソッドmethodを呼び出す:

  • PythonはまずクラスDでメソッドを探し、そこに見つけるよ。
  • メソッドD.method()は実行され、"D"を出力するよ。
  • 次にsuper().method()が呼ばれ、次のメソッドを呼び出すためにMROに従うんだ。

2. クラスBのメソッドmethodを呼び出す:

  • MROによると、次のクラスはDの後にBだよ。
  • メソッドB.method()は実行され、"B"を出力するよ。
  • 次にsuper().method()が呼ばれ、次のメソッドを呼び出すためにMROに従うんだ。

3. クラスCのメソッドmethodを呼び出す:

  • MROでBの後に来るのはCだよ。
  • メソッドC.method()は実行され、"C"を出力するよ。
  • 次にsuper().method()が呼ばれ、次のメソッドを呼び出すためにMROに従うんだ。

4. クラスAのメソッドmethodを呼び出す:

  • MROでCの後に来るのはAだよ。
  • メソッドA.method()は実行され、"A"を出力するよ。
  • その後super().method()が呼ばれるけど、Aには親のmethodobjectを除く)がないので、呼び出しはこれ以上の処理なしで終了するよ。
1
アンケート/クイズ
継承、レベル 16、レッスン 5
使用不可
継承
継承
コメント
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION