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

디스크립터와 __slots__ — 속성 아래의 규약

~22 min · descriptor, slots, memory, advanced

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

디스크립터는 속성 접근을 맡는 객체야

클래스 속성이 __get__, __set__, __delete__를 제공하면 obj.attr 접근이 그 메서드로 이어져. property도 이 규약으로 만들어져. 데이터 디스크립터는 인스턴스 딕셔너리보다 우선하고, 읽기 전용인 비데이터 디스크립터는 인스턴스 값에 가려질 수 있어.

__set_name__으로 자기 필드 이름을 안다

클래스 생성 시 디스크립터가 배치된 이름을 받아 인스턴스별 저장 위치와 오류 메시지를 만들 수 있어. 여러 필드의 검증 규칙을 재사용할 때 강력해.

__slots__는 허용 속성과 저장 모양을 고정해

보통 인스턴스의 __dict__를 없애 메모리를 줄이고 임의 속성 추가를 막아. 상속과 디스크립터가 섞이면 저장 위치를 신중히 설계해야 하며, 현대에는 @dataclass(slots=True)가 흔한 길이야.

Code

값을 검증하는 디스크립터·python
class Positive:
    def __set_name__(self, owner, name):
        self.attr = f"_{name}"

    def __get__(self, instance, owner):
        if instance is None:
            return self
        return getattr(instance, self.attr)

    def __set__(self, instance, value):
        if value <= 0:
            raise ValueError("양수여야")
        setattr(instance, self.attr, value)

class Account:
    balance = Positive()           # descriptor

    def __init__(self, initial):
        self.balance = initial      # descriptor 의 __set__ 사용

a = Account(100)
print(a.balance)                # 100  — __get__ 사용
a.balance = 200                 # __set__ 사용
print(a.balance)                # 200

try:
    a.balance = -50             # __set__ 검증
except ValueError as e:
    print(e)
property를 이루는 디스크립터 규약·python
# @property 와 동일 — Python 의 실제 구현이 descriptor 사용
class MyProperty:
    def __init__(self, fget):
        self.fget = fget

    def __get__(self, instance, owner):
        if instance is None:
            return self
        return self.fget(instance)

class Circle:
    def __init__(self, radius):
        self.radius = radius

    @MyProperty
    def area(self):
        return 3.14159 * self.radius ** 2

print(Circle(5).area)        # 78.54  — @property 처럼 작동
__slots__로 속성과 메모리 모양 고정하기·python
class Point:
    __slots__ = ("x", "y")

    def __init__(self, x, y):
        self.x = x
        self.y = y

p = Point(3, 4)
print(p.x, p.y)             # 3 4

# 새 속성을 추가할 수 없음
try:
    p.z = 5
except AttributeError as e:
    print(e)                # 'Point' object has no attribute 'z'

# 많은 인스턴스에 메모리 절약 큼
import sys

class Regular:
    def __init__(self, x, y):
        self.x, self.y = x, y

class Slotted:
    __slots__ = ("x", "y")
    def __init__(self, x, y):
        self.x, self.y = x, y

print(sys.getsizeof(Regular(1, 2).__dict__) + sys.getsizeof(Regular(1, 2)))
# Slotted 인스턴스는 __dict__ 없음 — 상당히 작음
dataclass에 slots 적용하기·python
from dataclasses import dataclass

@dataclass(slots=True)            # 3.10+
class Point:
    x: int
    y: int

p = Point(3, 4)
print(p)

try:
    p.z = 5
except AttributeError as e:
    print(e)

# dataclass 의 모든 혜택 + __slots__ 의 메모리 + 잠금

External links

Exercise

할당 값의 타입을 확인하는 TypedAttribute 디스크립터를 만들고 __set_name__으로 _count, _name 저장 이름을 정해. Thing의 count는 int, name은 str만 받게 시험한 뒤 __slots__를 더했을 때 디스크립터의 저장 방식과 어떤 상호작용이 생기는지 관찰해.

Progress

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

댓글 0

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

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