MRO

AI
gemma-4-31b
작성자
익명
작성일
2026.07.11
조회수
12
버전
v2

📋 문서 버전

이 문서는 2개의 버전이 있습니다. 현재 최신 버전을 보고 있습니다.

MRO (Method Resolution Order in Python

개요

MRO(Method Resolution Order, 메서드 해석 순서)는 Python에서 다중 상속을 사용할 때, 메서드나 속성이 어떤 순서로 탐색되고 호출되는지를 결정하는 규칙입니다. Python은 다중 상속을 지원하기 때문에, 한 클래스가 여러 부모 클래스를 상속받을 수 있으며, 이 경우 동일한 이름의 메서드가 여러 클래스에 정의되어 있을 수 있습니다. 이때, 어떤 부모 클래스의 메서드를 우선적으로 호출할지 결정하는 것이 MRO의 역할입니다.

Python 2.3 이후로는 C3 선형화(C3 Linearization) 알고리즘을 사용하여 MRO를 계산하며, 이는 일관성 있고 예측 가능한 상속 구조를 보장합니다.


MRO의 필요성

다중 상속은 코드 재사용성과 유연성을 높여주지만, 다음과 같은 모호성(ambiguity) 문제를 초래할 수 있습니다.

class A:
    def method(self):
        print("A의 method")

class B(A):
    def method(self):
        print("B의 method")

class C(A):
    def method(self):
        print("C의 method")

class D(B, C):
    pass

d = D()
d.method()  # 어떤 메서드가 호출될까?

위 코드에서 D 클래스는 BC를 동시에 상속받고 있으며, 두 클래스 모두 method()를 정의하고 있습니다. 이때 Python은 MRO에 따라 B.method()를 호출합니다. 그 이유는 D의 MRO가 D → B → C → A → object 순이기 때문입니다.


MRO 확인 방법

Python에서는 mro() 메서드 또는 __mro__ 속성을 통해 클래스의 MRO를 확인할 수 있습니다.

print(D.mro())
# 출력: [<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.A'>, <class 'object'>]

print(D.__mro__)
# 동일한 결과를 튜플 형태로 반환

또는 내장 함수 help(D)를 사용하여 클래스 정보와 함께 MRO를 확인할 수도 있습니다.


C3 선형화 알고리즘

MRO는 C3 선형화(C3 Linearization) 알고리즘에 따라 계산됩니다. 이 알고리즘은 다음과 같은 조건을 만족해야 합니다:

  1. 자식 클래스는 부모 클래스보다 먼저 온다 (Child before parent)
  2. 왼쪽에서 오른쪽으로 상속된 순서를 유지한다 (Left-to-right consistency)
  3. 모든 클래스가 단일 순서로 정렬되어야 하며, 충돌이 없어야 한다

C3 알고리즘의 기본 원리

클래스 C(B1, B2, ..., BN)의 MRO는 다음과 같은 공식으로 계산됩니다:

MRO(C) = [C] + merge(MRO(B1), MRO(B2), ..., MRO(BN), [B1, B2, ..., BN])

여기서 merge는 각 MRO 리스트와 부모 클래스 리스트를 조합하여 중복 없이 일관된 순서를 만드는 과정입니다. 구체적인 알고리즘은 복잡하지만, Python은 이를 내부적으로 자동으로 처리합니다.


예제: 복잡한 다중 상속 구조

다음은 더 복잡한 상속 구조에서 MRO가 어떻게 작동하는지 보여주는 예제입니다.

class X:
    pass

class Y:
    pass

class Z:
    pass

class A(X, Y):
    pass

class B(Y, Z):
    pass

class M(A, B, Z):
    pass

print(M.mro())

출력 결과:

[<class '__main__.M'>, <class '__main__.A'>, <class '__main__.X'>, <class '__main__.B'>, <class '__main__.Y'>, <class '__main__.Z'>, <class 'object'>]

이 결과는 C3 알고리즘이 X → B → Y 순서를 유지하면서도 충돌 없이 일관된 선형화를 수행했음을 보여줍니다.


super()와 MRO의 관계

super() 함수는 MRO를 기반으로 다음 클래스의 메서드를 호출합니다. 즉, 현재 클래스의 MRO 순서에서 자기 자신 다음에 오는 클래스의 메서드를 호출합니다.

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

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

이 순서는 D의 MRO(D → B → C → A)에 따라 super()가 다음 클래스로 이동하면서 메서드를 호출하기 때문입니다.


관련 참고 자료


결론

MRO는 Python의 다중 상속에서 핵심적인 역할을 하며, 메서드 호출의 일관성과 예측 가능성을 보장합니다. 개발자는 복잡한 상속 구조를 설계할 때 MRO를 이해하고, super()를 올바르게 사용함으로써 혼란을 방지해야 합니다. mro()를 적극 활용하여 클래스의 메서드 탐색 순서를 확인하고, 디버깅 및 설계 시 참고하는 것이 좋습니다.

C3 알고리즘의 상세 선택 메커니즘

merge 과정에서 다음 클래스를 선택하는 구체적인 메커니즘은 다음과 같습니다.

  1. 각 리스트의 첫 번째 요소(헤드, head)를 후보로 선정합니다.
  2. 후보 중 하나가 다른 모든 리스트의 꼬리(tail, 헤드를 제외한 나머지 부분)에 포함되어 있지 않은지 확인합니다.
  3. 위 조건을 만족하는 헤드가 있다면, 해당 클래스를 MRO 결과 리스트에 추가하고, 원래 리스트에서 해당 요소를 제거합니다.
  4. 다시 1번 단계로 돌아가 반복합니다. 만약 어떤 헤드도 조건을 만족하지 못하면 MRO 충돌이 발생한 것으로 간주합니다.

C3 알고리즘 단계별 추적 예시

class D(B, C) 구조에서 B(A), C(A)인 경우의 계산 과정입니다. - MRO(A) = [A, O] (O는 object) - MRO(B) = [B, A, O] - MRO(C) = [C, A, O]

단계 계산식 (merge 대상) 선택된 헤드 이유 결과 MRO
1 merge([B, A, O], [C, A, O], [B, C]) B B가 다른 리스트의 꼬리([A, O], [A, O], [C])에 없음 [D, B]
2 merge([A, O], [C, A, O], [C]) C C가 다른 리스트의 꼬리([O], [A, O])에 없음 [D, B, C]
3 merge([A, O], [A, O]) A A가 다른 리스트의 꼬리([O])에 없음 [D, B, C, A]
4 merge([O], [O]) O 마지막 남은 요소 [D, B, C, A, O]

MRO 충돌과 TypeError

C3 알고리즘은 일관성을 중시하므로, 상속 구조가 모순되어 선형화가 불가능한 경우 TypeError를 발생시킵니다. 이를 Inconsistent Hierarchy라고 합니다.

TypeError 발생 사례

가장 대표적인 사례는 부모 클래스들의 상속 순서가 자식 클래스에서 정의한 상속 순서와 정면으로 충돌할 때 발생합니다.

class A:
    pass

class B(A):
    pass

class C(A):
    pass

# B와 C의 상속 순서는 A를 공유하지만, 
# D가 B와 C를 상속받을 때 내부적인 제약 조건이 꼬이는 경우
class X:
    pass

class Y(X):
    pass

class Z(X):
    pass

# 의도적으로 모순된 구조 생성 (예: 부모의 MRO 순서가 자식의 상속 순서와 충돌)
class Bad(Y, Z): 
    pass

# 아래와 같이 상속 구조를 꼬아놓으면 TypeError가 발생합니다.
class A1: pass
class A2(A1): pass
class B1(A1): pass
class C1(A2, B1): pass
class D1(B1, A2): pass
class E1(C1, D1): pass 
# TypeError: Cannot create a consistent method resolution order (MRO) for bases A2, B1

이 오류는 Python이 "어떤 클래스를 먼저 탐색해야 할지 결정할 수 없는 논리적 모순"에 빠졌음을 의미하며, 설계 단계에서 상속 계층을 단순화하여 해결해야 합니다.

협력적 다중 상속 (Cooperative Multiple Inheritance)

super()는 단순히 '부모 클래스'를 호출하는 것이 아니라, MRO 리스트 상의 다음 순서(Next in MRO)를 호출하는 메커니즘입니다. 이를 통해 여러 클래스가 서로의 존재를 몰라도 MRO 체인에 따라 순차적으로 실행되는 '협력적 다중 상속'이 가능해집니다.

  • 핵심 원리: super().method() 호출 시, Python은 현재 클래스가 아닌 self 객체의 MRO 리스트를 참조하여 현재 클래스 바로 다음에 위치한 클래스의 메서드를 찾습니다.
  • 효과: 다이아몬드 상속 구조에서 최상위 클래스가 중복 호출되는 것을 방지하고, 모든 상속 경로의 메서드가 정확히 한 번씩 실행되도록 보장합니다.

MRO 설계 시 주의사항 및 베스트 프랙티스

다중 상속은 강력하지만 복잡성을 증가시킵니다. 이를 관리하기 위한 설계 원칙은 다음과 같습니다.

믹스인(Mixin) 패턴 활용

다중 상속의 부작용을 줄이기 위해, 독립적인 기능을 제공하는 믹스인 클래스를 사용하는 것이 권장됩니다.

  • 믹스인 클래스란?: 단독으로 인스턴스화되지 않고, 다른 클래스에 특정 기능(메서드)을 "혼입"시키기 위해 설계된 작은 클래스입니다.
  • 명명 규칙: 클래스 이름 끝에 Mixin을 붙여, 이 클래스가 독립적인 상태를 가지지 않고 기능 제공용임을 명시합니다. (예: JsonSerializableMixin, LoggingMixin)
  • 설계 원칙: 믹스인은 가급적 __init__ 메서드를 정의하지 않거나, 정의하더라도 반드시 super().__init__(*args, **kwargs)를 호출하여 MRO 체인을 깨뜨리지 않아야 합니다.

예측 가능한 설계를 위한 원칙

  1. 상속 깊이 최소화: 상속 계층이 깊어질수록 MRO 추적이 어려워지므로 가능한 단순하게 유지합니다.
  2. 단일 책임 원칙: 하나의 클래스가 너무 많은 역할을 상속받지 않도록 분리합니다.
  3. MRO 확인 습관화: 복잡한 다중 상속을 구현한 후에는 반드시 ClassName.mro()를 출력하여 의도한 순서대로 탐색되는지 검증합니다.
AI 생성 콘텐츠 안내

이 문서는 AI 모델(gemma-4-31b)에 의해 생성된 콘텐츠입니다.

주의사항: AI가 생성한 내용은 부정확하거나 편향된 정보를 포함할 수 있습니다. 중요한 결정을 내리기 전에 반드시 신뢰할 수 있는 출처를 통해 정보를 확인하시기 바랍니다.

이 AI 생성 콘텐츠가 도움이 되었나요?