본문 바로가기
C.W.K.
Stream
Lesson 02 of 07 · published

인스턴스 속성과 클래스 속성의 주인

~18 min · instance-attribute, class-attribute, mutable-default

Level 0호기심
0 XP0/93 lessons0/23 achievements
0/100 XP to next level100 XP to go0% complete

속성은 가까운 곳부터 찾는다

obj.x는 먼저 인스턴스를 보고, 없으면 클래스와 MRO를 따라가. 인스턴스에 x를 대입하면 같은 이름의 클래스 속성을 가릴 수 있지만 클래스의 값 자체가 바뀌는 건 아니야.

가변 클래스 속성은 모두가 공유해

클래스 본문에서 만든 리스트나 딕셔너리는 인스턴스마다 새로 생기지 않아. 한 객체가 append하면 다른 객체에서도 보여. 인스턴스별 모음은 __init__에서 self.items = []로 만들어.

의도적인 공유는 괜찮아

상수, 클래스 전체의 설정, 명시적인 레지스트리는 클래스 속성이 맞을 수 있어. obj.__dict__Class.__dict__를 보면 어디가 값을 소유하는지 확인할 수 있어.

Code

인스턴스에서 클래스로 이어지는 속성 탐색·python
class Robot:
    species = "robot"            # 클래스 속성 — 공유

    def __init__(self, name):
        self.name = name          # 인스턴스 속성 — 각자

r1 = Robot("R1")
r2 = Robot("R2")

print(r1.species, r2.species)    # robot robot   — 같은 값
print(r1.name, r2.name)          # R1 R2          — 다름

# 인스턴스에 설정하면 가림 — 클래스 안 바꿈
r1.species = "android"
print(r1.species, r2.species)    # android robot
print(Robot.species)             # robot          — 클래스 그대로
모든 인스턴스가 공유하는 가변 클래스 속성·python
# 안 좋음
class Bad:
    history = []                  # 클래스 속성 — 인스턴스 간 공유

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

    def log(self, event):
        self.history.append(event)

a = Bad("a")
b = Bad("b")
a.log("x")
b.log("y")
print(a.history)        # ['x', 'y']  <- a 가 b 의 log 봄!
print(b.history)        # ['x', 'y']
print(a.history is b.history)   # True — 같은 list

# 좋음 — __init__ 안에서 인스턴스에 할당
class Good:
    def __init__(self, name):
        self.name = name
        self.history = []         # 인스턴스 속성 — 매번 새 list

    def log(self, event):
        self.history.append(event)

a = Good("a")
b = Good("b")
a.log("x")
b.log("y")
print(a.history)        # ['x']
print(b.history)        # ['y']
클래스 전체가 공유할 상수·python
class Connection:
    DEFAULT_TIMEOUT = 30          # 클래스 속성 — 공유 상수
    MAX_RETRIES = 3

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

    def connect(self, timeout=None):
        if timeout is None:
            timeout = self.DEFAULT_TIMEOUT     # 인스턴스 통해 접근
        return f"connecting to {self.host} (timeout={timeout})"

c = Connection("localhost")
print(c.connect())               # 클래스의 30 사용
print(c.connect(timeout=10))     # 명시적 override
print(Connection.MAX_RETRIES)    # 클래스로 직접 접근
__dict__로 속성의 주인 확인하기·python
class Robot:
    species = "robot"

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

r = Robot("R1")

print(r.__dict__)              # {'name': 'R1'}            — 인스턴스 속성만
print("species" in r.__dict__) # False                     — 인스턴스에는 없음
print("species" in Robot.__dict__)  # True                  — 클래스에 있음

# r.species 작동하는 이유 — Python 이 클래스로 fallback
print(r.species)               # 'robot'

External links

Exercise

MAX_QUESTIONS = 10은 클래스 상수로, answers는 __init__에서 새 리스트로 만드는 Quiz를 구현해. 두 인스턴스의 답이 섞이지 않는지 확인한 뒤 answers를 일부러 클래스 속성으로 옮겨 같은 시험에서 공유 버그가 생기는지 관찰해.

Progress

Progress is local-only — sign in to sync across devices.
이 페이지에서 버그를 발견하셨거나 피드백이 있으세요?문제 신고

댓글 0

🔔 답글 알림 (로그인 필요)
로그인댓글을 남기려면 로그인해 주세요.

아직 댓글이 없어요. 첫 댓글을 남겨보세요.