프로그래밍/Python

[python] 클래스 변수와 멤버 변수의 차이

채윤아빠 2025. 12. 6. 17:00

이번 글에서는 파이썬 클래스에서 정의된 "클래스 변수(Class Variable)"와 "init()" 메서드 내에서 "self"를 통해 정의된 "멤버 변수(Member Variable)"의 차이점을 알아 보도록 하겠습니다.

클래스 변수와 멤버 변수 비교

class VariableTest:
    # 클래스 변수: 모든 인스턴스가 공유
    class_variable: int = 0

    def __init__(self) -> None:
        # 멤버 변수 (인스턴스 변수): 인스턴스마다 고유
        self.member_variable: int = 0

예제 코드에서 정의 위치별 구분

  • "class VariableTest:" 바로 아래 정의된 "class_variable"이 클래스 변수입니다.
  • "def init(self):" 내부에서 "self.member_variable"로 정의된 것이 멤버 변수(또는 인스턴스 변수)입니다.

클래스 변수와 멤버 변수 비교

구분 클래스 변수 (Class Variable) 멤버 변수 (Member Variable)
정의 위치 클래스 정의 바로 아래 (메서드 외부) "init()" 메서드 등 인스턴스 메서드 내부 ("self" 사용)
접근 주체 클래스 자체 또는 모든 인스턴스 특정 인스턴스
공유 여부 모든 인스턴스가 값을 공유합니다. 각 인스턴스마다 고유한 값을 가집니다.
사용 목적 모든 인스턴스에 공통적으로 적용되는
데이터, 상수 또는 인스턴스 개수 카운트 등
인스턴스마다 달라지는 "상태(state)"나
속성(attribute) 저장

클래스 변수와 멤버 변수간 차이점 예시

아래 예시 코드를 실행해 보면, 두 변수간 차이를 바로 확인하실 수 있습니다.

1. 초기 생성 및 접근

# 인스턴스 생성
instance1 = VariableTest()
instance2 = VariableTest()

print(f"--- 초기값 확인 ---")
print(f"instance1.class_variable: {instance1.class_variable}")
print(f"instance1.member_variable: {instance1.member_variable}")
print(f"instance2.class_variable: {instance2.class_variable}")
print(f"instance2.member_variable: {instance2.member_variable}")
print(f"클래스 직접 접근 (VariableTest.class_variable): {VariableTest.class_variable}")

2. 값 변경 시 차이

멤버 변수 변경 (고유 값)

"instance1"의 "member_variable"만 변경해도 "instance2"의 값은 영향을 받지 않습니다.

# instance1의 멤버 변수만 변경
instance1.member_variable = 10
instance2.member_variable = 20 # instance2의 값 변경

print(f"\n--- 멤버 변수 변경 후 ---")
print(f"instance1.member_variable: {instance1.member_variable}") # 10
print(f"instance2.member_variable: {instance2.member_variable}") # 20 (instance1의 변경에 영향받지 않음)


클래스 변수 변경 (공유 값)


⭐ 중요: 인스턴스 이름으로 클래스 변수에 값을 할당하면 발생하는 일

인스턴스("instance1")를 통해 클래스 변수("class_variable")에 값을 할당하면, 실제로는 클래스 변수의 값이 변경되는 것이 아니라, 해당 인스턴스("instance1")에 동명의 새로운 멤버 변수("class_variable")가 생성됩니다. 이 새로운 멤버 변수는 기존의 클래스 변수를 가립니다(shadowing).

# 인스턴스1을 통해 클래스 변수에 값을 할당 (새로운 멤버 변수 생성)
instance1.class_variable = 99 

print(f"\n--- 인스턴스로 클래스 변수 변경 시 (섀도잉) ---")
print(f"instance1.class_variable: {instance1.class_variable}")  # 99 (새로운 멤버 변수)
print(f"instance2.class_variable: {instance2.class_variable}")  # 0 (기존 클래스 변수 값 유지)
print(f"클래스 직접 접근 (VariableTest.class_variable): {VariableTest.class_variable}") # 0 (기존 클래스 변수 값 유지)


⭐ 중요: 클래스 자체를 통해 클래스 변수 변경 (모두 공유)

클래스 이름("VariableTest")을 통해 클래스 변수를 변경하면, 모든 인스턴스에서 이 변경된 값을 공유하게 됩니다.

# 클래스 자체를 통해 클래스 변수 변경 (모두 공유)
VariableTest.class_variable = 500

print(f"\n--- 클래스 자체로 클래스 변수 변경 후 ---")
# instance1은 이미 섀도잉된 '99'의 멤버 변수를 가지고 있으므로, 클래스의 변경에 영향받지 않습니다.
print(f"instance1.class_variable: {instance1.class_variable}")  # 99 (멤버 변수)

# instance2는 섀도잉된 멤버 변수가 없으므로, 변경된 클래스 변수 '500'을 참조합니다.
print(f"instance2.class_variable: {instance2.class_variable}")  # 500 (변경된 클래스 변수)

print(f"클래스 직접 접근 (VariableTest.class_variable): {VariableTest.class_variable}") # 500 (변경된 클래스 변수)

싱글톤 패턴에서의 클래스 변수 활용

"인스턴스가 단 하나뿐인 싱글톤 패턴이라면, 굳이 멤버 변수를 쓸 필요 없이 모두 클래스 변수로 관리해도 되지 않을까?"라는 의문이 생길 수 있습니다.

기술적 관점

싱글톤은 인스턴스가 하나이므로 클래스 변수를 사용해도 실행 결과는 동일하게 나타날 수 있습니다. 하지만 설계적 관점에서는 멤버 변수 사용을 강력히 권장합니다.

멤버 변수 사용이 권장되는 이유

  1. 역할의 분리 (Separation of Concerns)
  • 클래스 변수 : 인스턴스의 유일성을 유지하거나 생성된 객체 수를 관리하는 등 '클래스 자체의 메타 정보'를 담는 데 집중해야 합니다.
  • 멤버 변수 : 그 유일한 인스턴스(싱글통 패턴이 아니라면 각 인스턴스별로 독립적인)가 가져야 할 '실제 데이터(상태)'를 저장하는 데 사용되는 것이 객체 지향 원칙에 부합합니다.
  1. 일관성 있는 인터페이스 : 사용자는 인스턴스를 통해 데이터에 접근하는 방식("instance.data")에 익숙합니다. 싱글톤이라고 해서 클래스명으로 데이터에 접근하는 방식은 코드의 일관성을 해칠 수 있습니다.
  2. 유연한 확장성 : 향후 요구사항 변경으로 싱글톤 해제가 필요할 때, 멤버 변수로 설계된 코드는 로직 수정 없이 즉시 개별 객체의 독립성을 보장받을 수 있습니다.

맺는말

멤버 변수(인스턴스 변수)는 언제 이용하면 좋을까요? 객체마다 달라야 하는 속성을 저장할 때 이용합니다.
예를 들면, 사람 객체의 "이름", "나이", 자동차 객체의 "색상", "속도" 등 입니다.

그럼, 클래스 변수는 언제 이용하면 좋을까요? 모든 객체가 공유해야 하는 값을 저장할 때 이용합니다.
예를 들면, 모든 자동차 객체가 공유하는 "최대_허용_속도" (상수), 또는 생성된 객체의 총 개수를 세는 "count" 변수 등 입니다.

클래스 변수는 모든 인스턴스가 공통으로 사용하는 속성, 특히 상수나 카운터와 같은 용도로 이용할 때 효과가 가장 큽니다.




728x90
반응형