다중 상속에서 어떤 부모의 메서드가 호출될지 헷갈렸던 이유 — MRO
문제 발생
두 개의 부모 클래스를 다중 상속한 클래스에서 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를 보는데, B가 greet를 갖고 있으므로 거기서 멈춥니다 — C나 A까지 갈 필요가 없습니다. A가 B와 C의 공통 부모인데도, MRO 규칙상 B와 C가 A보다 먼저 탐색되기 때문에 A의 메서드는 B/C 둘 다 자기 것을 갖고 있지 않을 때만 도달합니다.
해결 방안
- 실제 MRO 순서가 궁금하면 클래스에서 직접 확인할 수 있습니다.
print(D.__mro__)
# (<class 'D'>, <class 'B'>, <class 'C'>, <class 'A'>, <class 'object'>)- 다중 상속을 쓸 때
super()가 "바로 다음 부모"가 아니라 **"MRO 순서상 다음 클래스"**를 가리킨다는 것을 이해해야 합니다 — 이 특성 덕분에 믹스인(mixin) 패턴에서 여러 클래스가 협력적으로super().method()를 연쇄 호출하는 것이 가능해집니다. - 다중 상속 구조가 복잡해질수록 MRO를 예측하기 어려워지므로, 실제로 필요한 것이 "여러 기능을 조합"이라면 믹스인 클래스를 명확한 역할 단위로 작게 나누고 문서화하는 것이 유지보수에 유리합니다.
- 다중 상속이 꼭 필요하지 않다면(예: 단순히 코드 재사용이 목적이라면) 컴포지션(has-a 관계, 객체를 필드로 갖는 방식)이 MRO의 복잡함 없이 더 명확한 대안이 되는 경우가 많습니다.
댓글0
댓글을 남기려면 로그인이 필요해요. 로그인
아직 댓글이 없어요. 첫 의견을 편하게 남겨 보세요.