learn

Python 클래스 vs 인스턴스 상태

Python 클래스 vs 인스턴스 상태

C++ 배경에서 Python을 볼 때 모호하게 느껴지는 이유: Python은 선언부 없이 런타임에 변수가 생겨서 클래스/인스턴스 구분이 눈에 잘 안 띈다.

C++와 1:1 대응

class Counter {
public:
    static int total;  // 클래스 상태 — 모든 인스턴스 공유
    int count;         // 인스턴스 상태 — 각 객체마다 독립
};
class Counter:
    total = 0          # 클래스 변수 ← C++ static int total

    def __init__(self):
        self.count = 0 # 인스턴스 변수 ← C++ int count

구조는 동일하다. 차이는 Python이 __init__ 안에서 self.count = 0을 처음 실행하는 순간 인스턴스 변수가 생긴다는 것 — C++처럼 클래스 본문에 선언부가 없다.

메모리 관점 비교

C++에서는 메모리 위치로 구분이 명확하다:

  • static 멤버 → BSS/data 영역 (프로그램 시작 시 할당)
  • 인스턴스 멤버 → 스택(값 타입) 또는 힙(new)

Python(CPython)에서는 모든 객체가 힙에 있다. 클래스 변수도 힙, 인스턴스 변수도 힙. 스택에는 참조(포인터)만 올라간다. 따라서 메모리 위치보다 생명주기와 공유 범위로 이해하는 것이 더 정확하다:

| | C++ 위치 | Python 위치 | 공유 범위 | |---|---|---|---| | 클래스 변수 | BSS/data 영역 | 힙 (클래스 객체 내부) | 모든 인스턴스 공유 | | 인스턴스 변수 | 스택 or 힙 | 힙 (인스턴스 객체 내부) | 해당 인스턴스만 |

메서드 데코레이터 선택 기준

| 데코레이터 | 첫 인자 | 언제 쓰나 | |---|---|---| | 없음 | self | 인스턴스 상태(self.x)가 필요할 때 | | @classmethod | cls | 클래스 자체를 다형적으로 다뤄야 할 때 (주로 팩토리 메서드) | | @staticmethod | 없음 | selfcls도 불필요한 유틸리티 |

주의: @classmethod는 단순히 "클래스 변수를 접근할 때"가 아니다. 클래스 변수는 @staticmethod에서도 Counter.total로 이름 직접 접근이 가능하다. @classmethod가 필요한 핵심은 서브클래스 상속 시 cls가 서브클래스를 가리켜야 할 때 — 팩토리 패턴이 대표적.

class Animal:
    @classmethod
    def create(cls):
        return cls()  # Dog.create() 하면 Dog 인스턴스가 만들어짐

class Dog(Animal):
    pass

Dog.create()  # cls = Dog → Dog() 반환

관련 노트