super(): 협력적 다중 상속의 핵심

Python super() 함수의 동작 원리, MRO 기반 위임 체계, 협력적 다중 상속에서의 올바른 사용법, **kwargs 패턴을 설명합니다.

· 4 min read · PALDYN Team

지난 글에서 다중 상속과 C3 선형화 알고리즘을 살펴보았습니다. super()는 그 MRO를 바탕으로 작동하는 함수로, 단순히 “부모 클래스”를 가리키는 것이 아니라 MRO 순서에서 현재 클래스 다음 클래스를 위임 대상으로 반환합니다. 이 미묘한 차이가 다중 상속에서 결정적인 역할을 합니다.

super()는 부모가 아니다

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

class B(A):
    def greet(self):
        return "B+" + super().greet()

class C(A):
    def greet(self):
        return "C+" + super().greet()

class D(B, C):
    def greet(self):
        return "D+" + super().greet()

D().greet()  # "D+B+C+A"

B.greet()에서 super()A가 아니라 **MRO의 다음 클래스인 C**를 가리킵니다. D.__mro__ = (D, B, C, A, object)이므로, B 입장에서 다음은 C입니다.

super() 동작 흐름

super() 동작 원리

super()는 두 개의 인자 (type, obj_or_type)로 정의됩니다. Python 3에서는 인자를 생략하면 컴파일러가 __class__ 셀 변수를 자동으로 삽입합니다.

# 이 두 코드는 동일합니다 (Python 3)
super().method()
super(__class__, self).method()

협력적 init

다중 상속에서 __init__이 제대로 협력하려면 모든 클래스가 super().__init__()을 호출해야 합니다.

협력적 init 체인

**kw 패턴을 쓰면 각 믹스인이 자신에게 필요한 인자만 꺼내 쓰고 나머지는 다음 클래스로 전달할 수 있습니다.

class TimestampMixin:
    def __init__(self, *, created_at=None, **kw):
        import datetime
        self.created_at = created_at or datetime.datetime.now()
        super().__init__(**kw)

class NamedMixin:
    def __init__(self, *, name: str, **kw):
        self.name = name
        super().__init__(**kw)

class Record(TimestampMixin, NamedMixin):
    def __init__(self, name: str) -> None:
        super().__init__(name=name)

체인이 끊기는 경우

class Bad(B, C):
    def greet(self):
        return "Bad+" + B.greet(self)  # super() 대신 직접 호출

B.greet(self)를 직접 호출하면 B의 내부 super()는 여전히 MRO 기준으로 C를 가리키지만, C.greet()에서의 super()가 건너뛰어질 가능성이 생깁니다. 복잡한 계층에서는 항상 super()를 사용해야 안전합니다.

super()와 클래스 메서드

class Parent:
    @classmethod
    def create(cls) -> "Parent":
        return cls()

class Child(Parent):
    @classmethod
    def create(cls) -> "Child":
        instance = super().create()  # Parent.create() 호출
        return instance

@classmethod에서도 super()는 동일하게 MRO를 따릅니다. cls는 여전히 호출된 클래스를 가리킵니다.

단일 상속에서도 super()

단일 상속에서도 super()를 쓰는 것이 권장됩니다.

class Base:
    def setup(self): pass

class Child(Base):
    def setup(self):
        super().setup()  # 나중에 믹스인 추가 시 안전
        self.extra = True

나중에 ChildBase 사이에 클래스를 삽입하더라도 super()를 쓰면 자동으로 올바른 클래스를 호출합니다.


지난 글: 다중 상속과 MRO: 다이아몬드 문제 해결

다음 글: 추상 기반 클래스: ABC와 abstractmethod


읽어주셔서 감사합니다. 😊