C.W.K.
Stream
Lesson 05 of 05 · published

Descriptor 와 __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

Descriptor — property / classmethod 등의 뒤 프로토콜

Descriptor 는 __get__, __set__, __delete__ 정의하는 모든 객체. 그런 객체가 클래스 속성으로 저장되면 인스턴스에서 접근 시 그 메서드들 통과 — 객체 직접 반환 X. @property 가 descriptor 로 구현. @classmethod, @staticmethod, 대부분 ORM 필드 타입도.

Data vs non-data descriptor

__set__ 가진 descriptor 는 *data descriptor*, 없는 건 *non-data descriptor*. 차이는 lookup 에 중요 — data descriptor 가 인스턴스 속성보다 이김, non-data 는 짐. property 가 data descriptor. 함수 (메서드 됨) 는 non-data — 그래서 self.method = something 으로 메서드 가릴 수 있어.

__set_name__ — 자기 이름 알기

Python 3.6+ 가 __set_name__(self, owner, name) 추가 — descriptor 가 클래스 속성에 할당될 때 호출. descriptor 한테 자기 이름 알려줘. 인스턴스별 state 관리 필요한 descriptor 에 유용 — 인스턴스에 관련 이름으로 저장 (descriptor x_x 로).

__slots__ — 메모리 + 속성 제어

디폴트 — 모든 Python 인스턴스가 __dict__ 가져 임의 속성 추가 가능. 클래스에 __slots__ = ("x", "y") 가 Python 한테 — 이 클래스 인스턴스는 *오직* 이 속성, __dict__ 없음. 메모리 절약 큼 (인스턴스마다 dict 없음). 다른 속성 설정 시도하면 AttributeError.

__slots__ 가 값어치 할 때

수백만 인스턴스 만들 때. 10-100 객체엔 __dict__ 오버헤드 노이즈. 백만이면 절약 진짜. dataclass 버전엔 @dataclass(slots=True) (3.10+). 주의 — __slots__ 클래스는 임의 속성 나중에 추가 X — 정확히 그게 목적, 그게 비용.

원칙: Descriptor 와 __slots__ 는 필요할 때 손에 닿는 도구 — 디폴트 X. 대부분 애플리케이션 코드 안 만짐. 라이브러리 / 프레임워크 코드가 더 자주. 보면 인식해, 진짜 use case 일 때 손에 닿아.

Code

단순 descriptor — 검증·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 가 어떻게 구축됐나 — stdlib 의 descriptor·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

# 새 속성 추가 X
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=True) — 현대 방법·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 descriptor 구현 — 할당 시 타입 강제. class Thing: count = TypedAttribute(int); name = TypedAttribute(str). t.count = "five"TypeError. __set_name__ 으로 descriptor 가 자기 이름 알고 인스턴스별 state 를 private 속성 이름 (_count, _name) 으로. 모든 path 테스트. 그 다음 클래스에 __slots__ 추가 — 뭐 변하는지 관찰 — __slots__ + descriptor 가 미묘하게 상호작용.

Progress

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

댓글 0

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

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