Programmingバックエンド開発者

Pythonにおけるクラス継承時の属性検索順序(MRO - Method Resolution Order)メカニズムはどのように機能しますか?メソッド解決の順序を理解することが重要な理由は何ですか?また、それを調べたり変更したりする方法はありますか?

Hintsage AIアシスタントで面接を突破

回答

メソッド解決順序(MRO)とは、Pythonにおいて、複数の継承がある場合にメソッドや属性を検索する際、どの順番でクラスが視察されるかを決定するメカニズムです。

C3線形化(C3 linearization) — これは、メソッド解決の順序を構築するために使用されるアルゴリズムです。MROを理解することは、複雑なクラス階層を設計する際に、あいまいさや予期しないメソッドの挙動を避けるために非常に重要です。

任意のクラスのMROを知るには、__mro__属性またはmro()関数を利用します:

class A: def hello(self): print('A') class B(A): def hello(self): print('B') class C(A): def hello(self): print('C') class D(B, C): pass print(D.__mro__) # (<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.A'>, <class 'object'>) obj = D() obj.hello() # 'B', なぜならBはMROでCの前にあるからです

MROを明示的に変更することはできませんが、基底クラスの順序を制御することは可能です。

悪意のある質問

クラスが複数の親から継承する場合、メソッドはどのような順序で呼び出されますか?例えば:

class Base: def foo(self): print('Base') class Left(Base): def foo(self): print('Left') super().foo() class Right(Base): def foo(self): print('Right') super().foo() class Child(Left, Right): pass Child().foo()

多くの人は、「foo」がLeftから呼ばれ、その後Right、最後にBaseが呼ばれると予想しますが、実際にはそうです。しかし、基底クラスの順序を変更すると(class Child(Right, Left))、順序は変わります!

正しい回答:

呼び出し順序はMROで定義されているとおりです。最初の場合は、Left、Right、Baseです。MROは特定のアルゴリズム(C3線形化)に従って構築されます:

[Child, Left, Right, Base, object]

理解不足による実際のエラーの例


ストーリー

ロジックの多重継承、メソッドのオーバーライド

開発チームはミックスインのために多重継承を使用しました。しかし、基底クラスの順序が不適切であったため、ミックスインの共通ロジックが子クラスによって「オーバーライド」され、ビジネスロジックの一部が実行されなくなり、データの損失が発生しました。


ストーリー

親の間違ったメソッドが呼ばれる

終了メソッド(__del__)では、基底クラスのメソッドが呼ばれることを期待していました。しかし、MROの理解が誤っていたため、他のミックスインから継承されたメソッドが呼び出され、リソースのクリーンアップが行われませんでした。その結果、メモリリークが発生しました。


ストーリー

呼び出しチェーンを理解せずにsuper()を使用する

Python 3のプロジェクトで、クライアントは多重継承を伴うコードの一部を再構築しましたが、super()を通じての呼び出しを更新するのを忘れました。これにより、メソッドが自己再帰し続ける無限再帰の状況が発生しました。これは、不正確なメソッド解決順序が原因です。