본문으로 건너뛰기
개발 머꼬
개발 노트Python
hohyeon.dev18

__init__이 실행 안 되는 상황이 있다? __new__와의 차이

  • #Engineering Note
  • #Object Model
  • #Python

문제 발생

str을 상속한 커스텀 클래스를 만들면서 __init__에서 값을 검증하려고 했는데, 특정 상황에서 검증 로직이 실행되지 않는 것처럼 보였습니다.

원인 분석

Python 객체 생성은 두 단계로 나뉩니다.

  1. __new__(cls, ...): 실제로 새 객체를 메모리에 만들어 반환하는 정적 메서드입니다. object.__new__가 기본 구현이며, 이 단계에서 객체가 실제로 생성됩니다.
  2. __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__ 시점에는 이미 불변 값이 확정되어 있어 바꿀 수 없습니다.

해결 방안

  1. 일반적인 클래스(가변 상태를 갖는)는 __init__만 재정의하면 충분합니다 — __new__를 건드릴 필요가 거의 없습니다.
  2. 불변 타입을 상속해 생성 시점에 값을 검증/가공해야 한다면 __new__를 재정의합니다.
class PositiveInt(int):
    def __new__(cls, value):
        if value <= 0:
            raise ValueError("양수만 허용")
        return super().__new__(cls, value)
  1. 싱글턴 패턴을 구현할 때도 __new__가 이미 존재하는 인스턴스를 반환하도록 만드는 방식을 씁니다 — __init__만으로는 "새 인스턴스를 아예 안 만드는" 걸 구현할 수 없습니다.
  2. 메타클래스(__call__을 재정의)도 비슷한 목적(클래스 생성 과정 제어)으로 쓰이지만 __new__/__init__보다 훨씬 드물게 필요합니다 — 정말 필요한 경우가 아니면 메타클래스까지 갈 필요는 없습니다.

공식 문서

마지막 수정

좋아요북마크

댓글0

아직 댓글이 없어요. 첫 의견을 편하게 남겨 보세요.