__init__이 실행 안 되는 상황이 있다? __new__와의 차이
문제 발생
str을 상속한 커스텀 클래스를 만들면서 __init__에서 값을 검증하려고 했는데, 특정 상황에서 검증 로직이 실행되지 않는 것처럼 보였습니다.
원인 분석
Python 객체 생성은 두 단계로 나뉩니다.
__new__(cls, ...): 실제로 새 객체를 메모리에 만들어 반환하는 정적 메서드입니다.object.__new__가 기본 구현이며, 이 단계에서 객체가 실제로 생성됩니다.__init__(self, ...):__new__가 반환한 객체를 받아서 초기화만 담당합니다. 객체는 이미 만들어진 상태입니다.
ClassName(...)을 호출하면 실제로는 먼저 __new__가 호출되어 객체를 만들고, 그 객체가 호출한 클래스의 인스턴스라면 그 다음 __init__이 호출됩니다. __new__가 다른 타입의 객체를 반환하면 __init__은 아예 호출되지 않습니다 — 이게 __init__이 실행 안 되는 것처럼 보이는 이유입니다.
class Foo:
def __new__(cls):
return "그냥 문자열" # Foo의 인스턴스가 아님
def __init__(self):
print("init 호출됨") # 절대 출력되지 않음
Foo() # "init 호출됨"이 출력 안 됨str, int, tuple 같은 불변 타입은 값이 객체 생성 시점에 확정되어야 하므로, 이를 상속해서 값 검증/변형을 하려면 __init__이 아니라 __new__에서 처리해야 합니다 — __init__ 시점에는 이미 불변 값이 확정되어 있어 바꿀 수 없습니다.
해결 방안
- 일반적인 클래스(가변 상태를 갖는)는
__init__만 재정의하면 충분합니다 —__new__를 건드릴 필요가 거의 없습니다. - 불변 타입을 상속해 생성 시점에 값을 검증/가공해야 한다면
__new__를 재정의합니다.
class PositiveInt(int):
def __new__(cls, value):
if value <= 0:
raise ValueError("양수만 허용")
return super().__new__(cls, value)- 싱글턴 패턴을 구현할 때도
__new__가 이미 존재하는 인스턴스를 반환하도록 만드는 방식을 씁니다 —__init__만으로는 "새 인스턴스를 아예 안 만드는" 걸 구현할 수 없습니다. - 메타클래스(
__call__을 재정의)도 비슷한 목적(클래스 생성 과정 제어)으로 쓰이지만__new__/__init__보다 훨씬 드물게 필요합니다 — 정말 필요한 경우가 아니면 메타클래스까지 갈 필요는 없습니다.
댓글0
댓글을 남기려면 로그인이 필요해요. 로그인
아직 댓글이 없어요. 첫 의견을 편하게 남겨 보세요.