본문 바로가기
Artificial Intelligence/60. Python

[PYTHON] 객체 생성의 비밀: __new__와 __init__의 5가지 차이와 해결 방법

by Papa Martino V 2026. 3. 30.
728x90

__new__와 __init__의 차이
__new__와 __init__의 차이

 

 

파이썬을 깊이 있게 공부하다 보면 "객체는 어떻게 만들어지는가?"라는 본질적인 질문에 마주하게 됩니다. 단순히 ClassName()을 호출하는 행위 이면에는 파이썬의 데이터 모델이 정의한 정교한 메커니즘이 숨어 있습니다. 특히 __new____init__은 그 중심에 있는 핵심 메서드입니다. 본 가이드는 단순한 문법 설명을 넘어, 실무에서 마주치는 복잡한 인스턴스화 문제를 해결하기 위한 전문적인 지식을 제공합니다.


1. 객체 생성의 2단계: __new__ vs __init__

흔히 입문자들은 __init__이 생성자(Constructor)라고 배우지만, 엄밀히 말하면 __init__초기화자(Initializer)입니다. 실제 객체를 메모리에 할당하고 생성하는 생성자 역할은 __new__가 담당합니다.

비교 항목 __new__ (생성자) __init__ (초기화자)
호출 시점 인스턴스 생성 시 가장 먼저 호출 인스턴스가 생성된 후 호출
메서드 성격 정적 메서드 (암시적 staticmethod) 인스턴스 메서드
첫 번째 인자 cls (클래스 자신) self (생성된 인스턴스)
반환 값 생성된 인스턴스 객체 (반드시 반환 필요) 반환값 없음 (None만 허용)
주요 목적 불변 객체 상속 및 메타프로그래밍 인스턴스 속성 설정 및 상태 초기화

2. Python 인스턴스화의 내부 흐름 (Flow)

파이썬에서 obj = MyClass()가 실행될 때 내부적으로 발생하는 순서는 다음과 같습니다.

  1. MyClass.__new__(MyClass, *args, **kwargs)가 호출되어 새로운 객체 인스턴스를 생성합니다.
  2. __new__MyClass의 인스턴스를 반환하면, 비로소 파이썬 인터프리터가 __init__을 호출합니다.
  3. 이때 __new__가 반환한 객체가 __init__self 인자로 전달됩니다.

3. 실무 중심의 7가지 활용 Example

현업 시니어 개발자들이 __new__를 활용해 복잡한 설계 문제를 해결하는 방식들을 코드로 살펴보겠습니다.

Example 1: 싱글톤(Singleton) 패턴 구현

애플리케이션 전체에서 단 하나의 인스턴스만 유지해야 하는 경우, __new__가 최적의 해결책입니다.

class DatabaseConnection:
    _instance = None

    def __new__(cls, *args, **kwargs):
        if cls._instance is None:
            print("Creating new instance...")
            cls._instance = super().__new__(cls)
        return cls._instance

    def __init__(self, db_url):
        self.db_url = db_url

# 실행 테스트
db1 = DatabaseConnection("url_1")
db2 = DatabaseConnection("url_2")
print(db1 is db2)  # True

Example 2: 불변 클래스(Immutable Class)의 상속 및 변형

tuple이나 int 같은 불변 객체를 상속받아 수정하고 싶을 때는 __init__이 아닌 __new__에서 값을 결정해야 합니다.

class PositiveInteger(int):
    def __new__(cls, value):
        # 음수 값이 들어오면 절댓값으로 변환하여 생성
        return super().__new__(cls, abs(value))

# 실행 테스트
p_int = PositiveInteger(-10)
print(p_int)  # 10

Example 3: 팩토리(Factory) 패턴을 통한 동적 인스턴스 반환

입력 값에 따라 서로 다른 서브클래스의 인스턴스를 반환하도록 제어할 수 있습니다.

class Shape:
    def __new__(cls, sides):
        if sides == 3:
            return super().__new__(Triangle)
        elif sides == 4:
            return super().__new__(Square)
        return super().__new__(cls)

class Triangle(Shape): pass
class Square(Shape): pass

# 실행 테스트
s1 = Shape(3)
print(type(s1))  # <class '__main__.Triangle'>

Example 4: 객체 생성 캐싱(Caching)을 통한 리소스 최적화

동일한 인자로 생성 요청이 올 경우 기존 객체를 재사용하여 메모리를 아낍니다.

class UserProfile:
    _cache = {}

    def __new__(cls, user_id):
        if user_id not in cls._cache:
            cls._cache[user_id] = super().__new__(cls)
        return cls._cache[user_id]

    def __init__(self, user_id):
        self.user_id = user_id

Example 5: 입력 값에 따른 유효성 검사 및 생성 거부

특정 조건이 만족되지 않을 때 아예 객체 생성을 막는 로직을 구현합니다.

class ValidatedUser:
    def __new__(cls, username):
        if not username:
            print("Invalid Username. Access Denied.")
            return None
        return super().__new__(cls)

# 실행 테스트
user = ValidatedUser("")
print(user)  # None

Example 6: API 응답 모델의 자동 매핑

수신된 데이터 포맷에 따라 동적으로 속성을 제어하는 고도화된 방식입니다.

class ApiResponse:
    def __new__(cls, data):
        instance = super().__new__(cls)
        if isinstance(data, dict):
            for key, value in data.items():
                setattr(instance, key, value)
        return instance

# 실행 테스트
res = ApiResponse({"status": "success", "code": 200})
print(res.status)  # success

Example 7: 메타클래스를 활용한 클래스 생성 로직 제어

클래스가 만들어지는 과정 자체를 추적하여 디버깅 정보를 남깁니다.

class DebugMeta(type):
    def __call__(cls, *args, **kwargs):
        print(f"[LOG] Instantiating {cls.__name__}...")
        return super().__call__(*args, **kwargs)

class MyService(metaclass=DebugMeta):
    pass

# 실행 테스트
service = MyService() # 로그 출력

4. 주의사항: __new__를 남용하지 마세요

대부분의 비즈니스 로직은 __init__만으로 충분합니다. __new__는 코드의 복잡도를 높이고 가독성을 떨어뜨릴 수 있으므로, 반드시 필요한 경우(싱글톤, 불변 상속, 팩토리 등)에만 제한적으로 사용하는 것이 파이써닉(Pythonic)한 개발 방법입니다.


5. 내용의 출처 및 참고 자료

  • Python Software Foundation. "Data Model - Customizing instance creation." Official Docs.
  • Fluent Python: Clear, Concise, and Effective Programming by Luciano Ramalho.
  • Effective Python: 90 Specific Ways to Write Better Python by Brett Slatkin.
  • Real Python - "Python's __new__ vs __init__: What's the Difference?"
728x90