Python 다중 상속에서 super()를 호출했더니 예상한 부모와 다른 클래스의 메서드가 실행됩니다. 호출 순서는 어떻게 정해지며 협력적으로 초기화하려면 무엇을 맞춰야 하나요?
잠깐, 답변을 보기 전에
먼저 내 말로 설명해 볼까요?
답변과 점검 포인트 펼치기답변 접기
구두 답변
정의를 외우기보다, 원리와 조건을 연결해 설명해 보세요.
다중 상속에서 메서드 탐색 순서는 클래스의 MRO(Method Resolution Order)에 저장되며, Python 3에서는 C3 선형화 규칙으로 계산됩니다. C3는 자식 클래스가 부모보다 먼저 나오고, 각 부모 목록의 순서를 보존하며, 같은 클래스를 중복 방문하지 않는 일관된 순서를 만들려고 합니다. 그래서 단순히 왼쪽 부모 트리를 끝까지 훑는 방식이 아니며, 모순된 상속 그래프는 클래스 정의 시 TypeError가 됩니다.
다음 구현을 찾는 MRO
class A:
def f(self): print("A")
class B(A):
def f(self): print("B"); super().f()
class C(A):
def f(self): print("C"); super().f()
class D(B, C):
pass
print([cls.__name__ for cls in D.__mro__])
D().f()
출력은 ['D', 'B', 'C', 'A', 'object']와 B, C, A입니다. 여기서 super()는 “내 부모 A”가 아니라 실제 객체 타입의 MRO에서 현재 메서드를 정의한 클래스 뒤를 따라 다음 메서드 구현을 찾습니다. 따라서 B가 super().f()를 호출하면 C로 이어지고, 각 클래스가 협력적으로 super()를 호출해야 다이아몬드 구조의 모든 구현이 한 번씩 실행됩니다.
협력적 초기화
실무에서는 다중 상속을 쓰더라도 모든 참여 클래스의 메서드 시그니처와 super() 호출 규칙을 맞추겠습니다. 한 클래스가 부모 메서드를 이름으로 직접 호출하면 다음 MRO를 건너뛰어 중복 실행이나 일부 초기화 누락이 생길 수 있습니다. 반대로 서로 협력하지 않는 외부 클래스들을 억지로 섞기보다 합성으로 경계를 나누는 편이 예측 가능할 때가 많습니다.
선택 기준과 검증
협력적 초기화에서는 각 mixin이 자신이 소비한 인자만 처리하고 남은 **kwargs를 다음 super()에 넘겨야 합니다. 이름 충돌이나 한 클래스의 직접 부모 호출은 MRO가 올바른데도 초기화를 누락시킬 수 있습니다. MRO 출력뿐 아니라 각 클래스의 부작용 횟수, 필수 상태, 예외 시 부분 초기화를 확인하겠습니다.
C3는 부모 목록의 순서뿐 아니라 이미 정한 상위 클래스의 순서를 하위 클래스에서 모순되게 뒤집지 않는 단조성도 유지합니다. 예를 들어 한 부모가 X보다 Y를 뒤에 요구하고 다른 부모가 반대 순서를 요구하면 자식의 일관된 MRO를 만들 수 없어 클래스 정의가 실패할 수 있습니다. 단순한 깊이 우선 탐색의 방문 순서와 C3 선형화를 구분하는 이유입니다.
모든 협력 메서드가 kwargs를 반드시 써야 하는 것은 아닙니다. 같은 고정 시그니처를 약속할 수도 있고, 키워드 인자를 각 mixin이 소비하는 규칙을 만들 수도 있습니다. 마지막에 object.__init__까지 남은 인자를 무작정 넘기면 오류가 날 수 있으므로 각 클래스의 소비 범위와 끝 조건을 확인해야 합니다. super 호출 횟수는 문법이 자동으로 보장하지 않으므로 누락·중복 호출과 예외 중 부분 초기화도 테스트하겠습니다.
득점 포인트
이해가 드러나는 지점을 확인해 보세요.
- C3 MRO와 super의 다음 구현 탐색을 설명한다.
- 협력적 초기화의 시그니처·인자 전달 계약을 제시한다.
- 직접 부모 호출과 합성의 위험을 비교한다.
감점 포인트
놓치기 쉬운 조건과 오해를 짚어 보세요.
- super가 언제나 바로 왼쪽 부모를 직접 호출한다고 말한다.
- MRO가 메서드 구현을 복사해 하나로 합친다고 설명한다.
- 한 클래스만 super를 호출해도 모든 부모 초기화가 자동 실행된다고 가정한다.
더 파고들 거리
하나 더 질문하며 이해의 범위를 넓혀 보세요.
- 다이아몬드 상속의 C3 MRO를 계산합니다. 후보 head를 tail과 비교하는 규칙은 왜 필요한가요?
__init__에서 kwargs를 협력적으로 전달할 때 남은 인자를 어떻게 검증할까요?- 모순된 상속 그래프가 TypeError가 되는 이유를 어떤 최소 예로 설명할까요?
득점·감점 포인트는 학습용 자기 점검 기준이며, 특정 회사의 실제 채점표가 아닙니다.
원리부터 공부하고 싶다면
답변을 보충하는 개념·예제·슈도코드를 읽어 보세요.