클래스, 인터페이스, 메타프로그래밍
- 4.1 함수를 인터페이스로 사용
- 4.2 isinstance 대신 다형성
- 4.3 functools.singledispatch
- 4.4 dataclasses로 가벼운 클래스
- 4.5 @classmethod 다형성
- 4.6 super로 부모 초기화
- 4.7 믹스인 클래스로 기능 합성
- 4.8 비공개보다 공개 애트리뷰트
- 4.9 불변 dataclasses
- 4.10 collections.abc 상속
- 4.11 setter/getter 대신 일반 애트리뷰트
- 4.12 @property로 애트리뷰트 리팩토링
- 4.13 디스크립터로 재사용 가능한 @property
- 4.14 __getattr__, __getattribute__, __setattr__
- 4.15 __init_subclass__로 서브클래스 검증
- 4.16 __init_subclass__로 서브클래스 등록
- 4.17 __set_name__으로 클래스 애트리뷰트 주석
- 4.18 클래스 본문 정의 순서와 애트리뷰트 관계
- 4.19 메타클래스 대신 클래스 데코레이터
파이썬 클래스 설계의 핵심은 "자유도가 높다"는 게 장점이자 함정이라는 거야. 클래스를 만드는 것부터 메타프로그래밍까지, 올바른 추상화 수준을 고르는 법을 정리하지.
간단한 인터페이스가 필요하면 클래스를 만들 필요 없이 함수를 쓰면 돼. 파이썬에서 함수는 일급 객체니까 콜백으로 넘기고, defaultdict에 팩토리로 넣고, 상태가 필요하면 __call__을 정의한 클래스로 올리면 되지. isinstance로 타입 검사해서 분기하는 코드는 열린-닫힌 원칙을 위반하는 거야 — 새 타입이 추가될 때마다 분기문을 고쳐야 하니까. 각 클래스에 같은 이름의 메서드를 정의하는 다형성이 답이고, 클래스를 수정할 수 없는 경우에는 **singledispatch**로 함수형 다형성을 쓸 수 있어.
데이터를 담는 클래스에는 **@dataclass**가 기본 도구야. __init__, __repr__, __eq__를 자동 생성해주고, frozen=True로 불변 객체도 만들 수 있지 — 딕셔너리 키로 쓸 수 있고 스레드 안전하고. 팩토리가 필요하면 **@classmethod**를 쓰면 서브클래스에서도 다형적으로 동작해. 다중 상속 환경에서는 반드시 **super()**를 써야 다이아몬드 상속 문제를 피할 수 있고, 기능을 조합할 때는 상태를 갖지 않는 믹스인 클래스가 실용적이야.
__name 형태의 이름 맹글링으로 비공개 애트리뷰트를 만드는 건 거의 쓸 일이 없어. 파이썬의 철학은 "우리는 모두 동의하는 성인"이거든. **밑줄 하나(_name)**로 관례를 따르면 충분하고, 커스텀 컨테이너를 만들 때는 list를 직접 상속하는 것보다 **collections.abc**의 추상 클래스를 상속하면 필요한 최소 인터페이스가 명확해져.
애트리뷰트 접근을 제어하는 방법도 단계가 있어. 자바에서 온 사람들이 습관적으로 get_name(), set_name() 메서드를 만드는데, 파이썬에서 이건 안티패턴이야. 그냥 일반 애트리뷰트로 시작하고, 나중에 검증이나 변환 로직이 필요해지면 **@property**로 업그레이드하면 돼. 호출자 코드를 바꿀 필요가 없다는 게 파이썬의 강점이지. resistor.ohms = -5라고 쓰면 setter가 자동으로 호출되어 검증이 일어나는 거야. 같은 @property 로직이 여러 클래스에서 반복되면 디스크립터로 올려서 재사용하면 되고, __getattr__을 쓰면 지연 로딩 같은 동적 애트리뷰트 접근도 깔끔하게 구현할 수 있어.
서브클래스를 검증하거나 자동으로 등록해야 할 때, 예전에는 메타클래스가 유일한 방법이었는데, 이제는 **__init_subclass__**가 대부분의 용도를 대체해. 클래스 정의 시점에 호출되니까 잘못된 서브클래스는 임포트할 때 바로 에러가 나고, 플러그인 패턴처럼 서브클래스를 레지스트리에 자동 등록하는 것도 간단하지. **__set_name__**과 함께 쓰면 디스크립터가 자신이 할당된 애트리뷰트 이름을 알 수 있어서 더 강력해져.
메타클래스가 강력한 건 맞지만, 합성이 어렵고 클래스당 하나만 지정할 수 있어서 두 라이브러리가 각각 메타클래스를 요구하면 충돌해. 클래스 데코레이터는 여러 개를 겹쳐 쓸 수 있고, 이해하기 쉽고, 상속 관계와 독립적이야. 메타클래스가 반드시 필요한 경우는 극히 드물거든.
정리
4장 읽고 기억할 거 세 가지:
- 간단한 인터페이스는 함수로, isinstance 체인은 다형성으로 대체하라. dataclass가 클래스 설계의 기본 도구이고, 비공개 애트리뷰트(
__)는 거의 쓸 일이 없다. - 일반 애트리뷰트로 시작하고, 필요할 때 @property, 반복되면 디스크립터로 올려라. setter/getter 메서드는 파이썬에서 안티패턴이다.
- __init_subclass__가 메타클래스의 대부분의 용도를 대체한다. 메타클래스 대신 클래스 데코레이터를 쓰면 합성이 가능하고 이해하기 쉽다.