개요

파이썬은 다중 상속을 지원하며 동일한 이름의 메소드가 여러 부모에 존재할 때 어떤 구현을 선택할지 결정해야 함 이 결정 규칙이 MRO(Method Resolution Order)로, 클래스 계층 전체에서 메소드를 탐색하는 순서를 정의함 MRO가 없다면 동일 시그니처 메소드가 교차 상속되는 상황에서 해석 모호성, 이른바 death diamond 문제가 발생함

핵심 개념

  • MRO의 본질은 메소드 탐색 순서의 선형화
  • 파이썬은 C3 linearization 알고리즘으로 일관된 순서를 계산함
  • 규칙 요약
    • 자식 클래스가 먼저
    • 왼쪽에서 오른쪽으로 명시한 부모 순서를 존중
    • 각 부모의 MRO를 안정적으로 병합하여 일관성 유지
  • 클래스 속성 mro 또는 클래스 메소드 mro()로 확인 가능

동작 원리

  • 탐색 순서 기본 형태
    • 자식 클래스 → 명시된 순서의 부모들 → 각 부모의 상위 클래스들 → 최상위 object 순으로 선형화
  • C3 linearization은 다음 제약을 동시에 만족하는 유일한 순서를 생성
    • 자식은 항상 부모보다 앞선다
    • 부모 목록의 왼쪽 우선 순서를 보존한다
    • 부모들의 MRO 순서를 보존한다
  • 이 규칙으로 동일 이름 메소드가 다수 존재해도 첫 번째로 만나는 구현이 선택됨

사용법과 간단 예시

다중 상속에서 실제 탐색 순서를 눈으로 확인하는 것이 가장 빠름

class Human:
    def say(self):
        print("안녕")

class Mother(Human):
    def say(self):
        print("엄마")

class Father(Human):
    def say(self):
        print("아빠")

class Son(Mother, Father):
    pass

print([c.__name__ for c in Son.mro()])
# ['Son', 'Mother', 'Father', 'Human', 'object']

Son().say()
# 엄마
  • Son 자체에 say가 없으면 MRO에서 Mother가 Father보다 앞이므로 Mother.say 선택
  • 부모 선언 순서를 바꾸면 우선순위도 바뀜

super 동작의 핵심은 실제 부모가 아니라 MRO에서의 다음 클래스라는 점

class Mother(Human):
    def say(self):
        super().say()  # MRO에서 다음 클래스 대상

class Father(Human):
    def say(self):
        print("아빠")

class Son(Mother, Father):
    pass

Son().say()
# 아빠
  • Mother의 실질 부모는 Human이지만 MRO에서 Mother 다음은 Father
  • super()는 이 선형화된 다음 대상을 호출하므로 Father.say가 실행됨

주의사항과 실패 사례

C3 규칙으로 일관된 순서를 만들 수 없으면 클래스 정의 시점에 TypeError 발생

사례 1

class A: pass
class B(A): pass
class C(A): pass
class D(A, B, C): pass  # TypeError: 일관된 MRO 불가
  • D에서 A를 가장 왼쪽에 두었는데 B와 C가 다시 A를 상속하여 순서 제약이 충돌

사례 2

class A: pass
class B: pass
class C(A, B): pass
class D(B, A): pass
class E(C, D): pass  # TypeError: 순서를 정할 수 없음
  • 부모 쌍의 순서가 교차되어 하위에서 병합 불가 상황 발생

회피 팁

  • 부모 클래스 목록의 순서를 신중히 설계
  • 동일 역할 믹스인은 왼쪽에서 오른쪽 우선 순위가 기대와 일치하도록 정렬
  • 다이아몬드 계층이 복잡해지면 상속보다 합성 사용 고려
  • super 호출은 다중 상속 전제 하에 협력적 메소드 체인으로 작성하는 패턴 사용
    • 오버라이드 메소드에서 super를 호출하고 동일 시그니처 유지

확인과 디버깅 팁

  • 클래스.mro() 혹은 클래스.__mro__로 즉시 확인
  • 인스턴스 메소드 해석은 dir와 getattr로 실제 바인딩 대상 점검
  • super의 대상이 예상과 다르면 MRO 출력으로 체인 순서 재확인

마무리

MRO는 다중 상속 환경에서 메소드 선택을 결정하는 선형화 규칙이며 파이썬은 C3 알고리즘으로 일관성과 예측 가능성을 보장함 super는 상속 트리의 직접 부모가 아니라 MRO에서의 다음 클래스에 위임한다는 점이 핵심 복잡한 계층에서 모호성이 생기면 컴파일 타임에 TypeError로 드러나므로, 부모 순서 설계와 협력적 super 패턴으로 예방하는 전략이 안전함

참고자료