본문으로 건너뛰기
개발 머꼬
개발 노트Python
hohyeon.dev25

다중 상속에서 어떤 부모의 메서드가 호출될지 헷갈렸던 이유 — MRO

  • #Engineering Note
  • #Object Model
  • #Python

문제 발생

두 개의 부모 클래스를 다중 상속한 클래스에서 super().greet()을 호출했는데, 예상했던 부모가 아니라 다른 부모의 메서드가 실행됐습니다.

class A:
    def greet(self):
        print("A")

class B(A):
    def greet(self):
        print("B")

class C(A):
    def greet(self):
        print("C")

class D(B, C):
    pass

D().greet()  # "B"가 출력됨 — A가 아니라

원인 분석

Python은 다중 상속에서 메서드를 찾는 순서를 **MRO(Method Resolution Order)**라는 규칙으로 결정합니다. 단순히 "부모를 순서대로 하나씩 본다"가 아니라 **C3 선형화(C3 linearization)**라는 알고리즘을 씁니다 — 각 클래스가 자신의 부모보다 먼저 오고, 여러 부모가 있으면 그 순서(선언 순서)가 유지되도록 일관된 순서를 계산합니다.

D(B, C)의 MRO는 D -> B -> C -> A -> object입니다. D 자신에게 greet가 없으므로 다음은 B를 보는데, Bgreet를 갖고 있으므로 거기서 멈춥니다 — CA까지 갈 필요가 없습니다. ABC의 공통 부모인데도, MRO 규칙상 BCA보다 먼저 탐색되기 때문에 A의 메서드는 B/C 둘 다 자기 것을 갖고 있지 않을 때만 도달합니다.

해결 방안

  1. 실제 MRO 순서가 궁금하면 클래스에서 직접 확인할 수 있습니다.
print(D.__mro__)
# (<class 'D'>, <class 'B'>, <class 'C'>, <class 'A'>, <class 'object'>)
  1. 다중 상속을 쓸 때 super()가 "바로 다음 부모"가 아니라 **"MRO 순서상 다음 클래스"**를 가리킨다는 것을 이해해야 합니다 — 이 특성 덕분에 믹스인(mixin) 패턴에서 여러 클래스가 협력적으로 super().method()를 연쇄 호출하는 것이 가능해집니다.
  2. 다중 상속 구조가 복잡해질수록 MRO를 예측하기 어려워지므로, 실제로 필요한 것이 "여러 기능을 조합"이라면 믹스인 클래스를 명확한 역할 단위로 작게 나누고 문서화하는 것이 유지보수에 유리합니다.
  3. 다중 상속이 꼭 필요하지 않다면(예: 단순히 코드 재사용이 목적이라면) 컴포지션(has-a 관계, 객체를 필드로 갖는 방식)이 MRO의 복잡함 없이 더 명확한 대안이 되는 경우가 많습니다.

공식 문서

마지막 수정

좋아요북마크

댓글0

아직 댓글이 없어요. 첫 의견을 편하게 남겨 보세요.