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
: クラスB
とC
でメソッドが見つからない場合、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
には親のmethod
(object
を除く)がないので、呼び出しはこれ以上の処理なしで終了するよ。
GO TO FULL VERSION