728x90
super()
함수
유지보수가 용이한 객체지향 파이썬 프로그래밍을 위해서는 super()
내장 함수의 일관된 사용이 절대적으로 중요
상속 관계에서 부모 클래스의 메서드를 호출할 때 사용
class LastUpdatedOrderedDict(OrderedDict):
def __setitem__(self, key, value):
super().__setitem__(key, value)
self.move_to_end(key)
오버라이드된 __init__()
메서드를 호출하는 것은 슈퍼클래스가 인스턴스를 초기화하는 데 필요한 일을 할 수 있게 하므로 특히 중요하다.
def __init__(self, a, b):
super().__init__(a, b)
... # 추가 초기화 코드
특히 다중 상속 구조에서 MRO를 따르게 해줌
- MRO: 클래스의 상속 구조에서 어떤 순서로 메서드를 찾을지 정해놓은 규칙
- 특히 다중 상속에서는 같은 이름의 메서드가 여러 부모 클래스에 있을 수 있으니까, 어떤 부모의 메서드를 먼저 호출할지가 중요
super()
는 부모 호출이 아니라 다음 MRO 항목 호출
python2
super(MyClass, self).method()
- MyClass`: 현재 클래스 -> MRO를 어디서부터 찾을지 결정
self
: 현재 인스턴스 -> MRO에서 다음 메서드를 어떻게 찾을지 결정- 반드시 명시해야함
python3
super().method()
- 둘 다 생략 가능
- 자동으로 현재 클래스와 인스턴스 추론
- 클래스 내부에서만 동작, 정의된 메서드가 클래스 스코프 안에 있어야 함
내장형 상속의 문제점
내장형 상속: 파이썬 기본 제공 타입(예: list, dict, str, int)을 상속해서 새로운 클래스를 만드는 것
class MyList(list):
pass
문제점
- 내부 메서드 간 연결 없음
- 메서드 오버라이딩(덮어쓰기)을 해도 다른 메서드 내부에서 우리가 오버라이드한 메서드를 호출하지 않음
- 예)
extend()
가 내부에서append()
를 호출하지 않음,append()
를 오버라이딩해도extend()
가 그걸 안 씀 - 설계 구조 자체의 문제임
- 일부 동작이 직접적으로 메서드를 호출하지 않음
__getitem__
,__setitem__
,__iter__
같은 걸 재정의해도, list나 dict 내부의 루프, 복사, 슬라이스 처리 같은 부분은 재정의한 메서드를 우회해서 동작할 수 있음- 파이썬 문법이 실제로는 C에서 직접 처리되므로 메서드가 무시됨
- 호환성과 유지보수 이슈
대안collections.UserList
, collections.UserDict
, collections.UserString
같은 랩핑(wrapping) 기반 클래스 상속
from collections import UserList
class MyList(UserList):
def append(self, item):
print(f"Appending {item}")
super().append(item)
- 모든 동작이 정의한 메서드를 거쳐서 발생하기 때문에 안정적
다중 상속과 메서드 결정 순서
다중 상속: 한 클래스가 여러 부모 클래스를 동시에 상속받는 구조
다이아몬드 문제: 다중 상속을 구현하는 언어는 슈퍼클래스들이 동일한 이름으로 메서드를 구현할때 발생하는 이름 충돌 문제
class A: ...
class B(A): ...
class C(A): ...
class D(B, C): ...
- D는 B와 C를 상속하고, B, C는 각각 A를 상속 → A가 중복 상속됨
A
/ \
B C
\ /
D
- D에서 A의 메서드를 호출하면 → 어느 경로로 A를 호출할지 모호해짐
MRO가 클래스들의 우선순위를 하나의 선형 리스트로 만들어줌
D.__mro__
(<class 'D'>, <class 'B'>, <class 'C'>, <class 'A'>, <class 'object'>)
- 파이썬은 이 순서를 따라 메서드를 탐색 -> 중복 호출 없이, 명확한 순서로만 진행
그래서 super()
중요!!!
cooperative inheritance (협조적 상속): 다중 상속 구조에서는 모든 클래스가 super()
를 사용할 것을 전제로 해야 함
믹스인 클래스
다중 상속에서 다른 클래스와 함께 사용해서 추가 기능을 제공하는 클래스(독립적인 재사용 목적)
- 독립 실행 불가: 자체적으로 인스턴스를 만들진 않고, 다른 클래스에 기능을 추가하는 용도
- 기능 단위: 작고 독립적인 기능 하나에 집중
- 다른 클래스에 끼워넣는 보조 클래스: 주로 메인 클래스와 함께 다중 상속 구조로 사용
- 상속 순서에 따라 MRO 영향 있음: 믹스인이
super()
를 사용하면 전체 MRO 흐름에 참여함
class JsonMixin:
def to_json(self):
import json
return json.dumps(self.__dict__)
class Animal:
def __init__(self, name):
self.name = name
class Dog(Animal, JsonMixin):
pass
dog = Dog("Charlie")
print(dog.to_json()) # {"name": "Charlie"}
Dog
는JsonMixin
에서to_json()
만 믹스해서 사용
주의 사항
- 상태(state) 갖지 말기 - 메인 클래스랑 충돌 예방
- 단일 책임 원칙 지키기
- Mixin이라고 명시해주기
믹스드인 활용 - 메서드 체이닝
class Step1Mixin:
def process(self, data):
print("Step 1")
data += " -> step1"
return super().process(data)
class Step2Mixin:
def process(self, data):
print("Step 2")
data += " -> step2"
return super().process(data)
class FinalStep:
def process(self, data):
print("Final step")
return data + " -> done"
class Chain(Step1Mixin, Step2Mixin, FinalStep):
pass
result = Chain().process("start")
print(result)
super().process()
가 MRO에 따라 다음 클래스의 process() 호출
Step 1
Step 2
Final step
start -> step1 -> step2 -> done
상속 처리
- 상속하기 전에 객체 구성을 먼저 생각해라
- 상속하는 이유를 명확히 해라
- ABC로 인터페이스임을 명시해라
- 코드 재사용하고싶으면 믹스인 써라
- 사용자에게 집합 클래스 제공해라
- 상속하도록 설계된 클래스만 상속해라
- 구상 클래스 상속 피해라
References
- 『 전문가를 위한 파이썬(2판) 』, 루시아누 하말류, 한빛미디어, Part 1 - Chapter 14
- 믹스인? 첨들었으나... 재밌군 믹스인으로 메서드 체이닝하는걸로 넘어가니 랭체인이 이런 원리를 기반으로 하나 생각이 들었다 재밌다
- 하지만 랭체인은 합성(composition) + invoke() 체이닝 방식으로 여기서말하는 믹스인 + MRO랑은 좀 다르다 함
- 책은 재미없음
728x90
'CS' 카테고리의 다른 글
[Python] 연산자 오버로딩 (1) | 2025.04.19 |
---|---|
[Python] 자료형 힌트 조금 더 알아보기 (0) | 2025.04.19 |
[Python] 인터페이스, 프로토콜, 추상 베이스 클래스 (1) | 2025.04.13 |
[Python] 시퀀스 특별 메서드 (0) | 2025.04.12 |
[Python] 파이썬다운 객체 (0) | 2025.04.07 |