디스크립터: __get__, __set__, __delete__
속성 액세스 제어를 위해 디스크립터 프로토콜을 구현합니다.
디스크립터: __get__, __set__, __delete__은(는) CoddyKit의 무료 Python Academy 강의입니다. 이것은 4개 중 3번째 강의입니다. 아래에서 전체 강의를 무료로 읽을 수 있으며, 내장 코드 에디터와 24/7 AI 튜터와 함께 브라우저에서 직접 실습할 수 있습니다. 이 강의는 Python Academy 학습 경로의 일부이며, 진행 상황이 웹과 CoddyKit 앱에 동기화됩니다. Python Academy 강의에는 총 4개의 강의가 포함되어 있습니다.
Descriptor란 무엇인가
디스크립터는 __get__, __set__ 또는 __delete__를 정의하고 클래스 속성으로 할당되는 객체입니다. Python은 속성에 접근할 때 이러한 메서드를 호출합니다.
class Descriptor:
def __get__(self, obj, objtype=None):
print(f"__get__ called, obj={obj}")
return 42
class MyClass:
attr = Descriptor()
print(MyClass().attr) # __get__ called ... 42데이터 디스크립터와 비데이터 디스크립터 비교
데이터 디스크립터는 __set__ 또는 __delete__를 정의하며 인스턴스의 __dict__보다 우선합니다. 비데이터 디스크립터는 __get__만 정의하며 __dict__에 우선권을 양보합니다.
class DataDesc:
def __get__(self, obj, t): return "data"
def __set__(self, obj, val): pass
class NonDataDesc:
def __get__(self, obj, t): return "nondata"
class C:
d = DataDesc()
n = NonDataDesc()
c = C()
c.__dict__["n"] = "instance" # shadows the non-data desc
c.__dict__["d"] = "instance" # does NOT shadow data desc
print(c.n) # instance
print(c.d) # data__get__ 시그니처
__get__(self, obj, objtype)에서 obj는 인스턴스입니다(클래스에서 접근하면 None). objtype은 클래스입니다.
class Verbose:
def __get__(self, obj, objtype=None):
if obj is None:
return self # accessed on class
return f"value for {obj!r}"
class Widget:
label = Verbose()
print(Widget.label) # <Verbose object>
print(Widget().label) # value for <Widget object>__set__과 유효성 검사
값을 저장하기 전에 유효성을 검사하거나 변환하려면 __set__(self, obj, value)를 구현하세요.
class PositiveInt:
def __set_name__(self, owner, name):
self.name = name
def __get__(self, obj, t):
return obj.__dict__.get(self.name)
def __set__(self, obj, val):
if not isinstance(val, int) or val <= 0:
raise ValueError(f"{self.name} must be a positive int")
obj.__dict__[self.name] = val
class Rect:
width = PositiveInt()
height = PositiveInt()
r = Rect()
r.width = 10
# r.width = -1 # ValueError__set_name__
Python 3.6 이상에서는 디스크립터가 클래스에 할당될 때 __set_name__(owner, name)을 호출하여 디스크립터가 자신의 속성 이름에 접근할 수 있게 합니다.
class Typed:
def __set_name__(self, owner, name):
self.public = name
self.private = "_" + name
def __get__(self, obj, t):
return None if obj is None else getattr(obj, self.private, None)
def __set__(self, obj, val):
setattr(obj, self.private, val)
class User:
name = Typed()
u = User()
u.name = "Alice"
print(u.name) # Alice속성 제거를 위한 __delete__
del obj.attr을 가로채려면 __delete__(self, obj)를 정의하세요.
class Protected:
def __set_name__(self, owner, name): self.name = name
def __get__(self, obj, t):
return obj.__dict__.get(self.name)
def __set__(self, obj, v): obj.__dict__[self.name] = v
def __delete__(self, obj):
raise AttributeError(f"Cannot delete {self.name}")
class Config:
host = Protected()
c = Config(); c.host = "localhost"
# del c.host # AttributeError함수는 비데이터 디스크립터입니다
함수는 __get__을 구현하여 인스턴스에서 접근할 때 바인딩된 메서드를 반환합니다. 이것이 Python에서 메서드 바인딩이 작동하는 방식입니다.
def greet(self):
return f"Hello from {self}"
class C: pass
C.greet = greet
c = C()
print(c.greet()) # Hello from <C object>classmethod와 staticmethod는 디스크립터입니다
classmethod와 staticmethod는 함수를 감싸고 __get__의 반환값을 변경하는 내장 디스크립터 클래스입니다.
class MyClass:
@classmethod
def from_string(cls, s):
return cls()
# classmethod.__get__ returns a bound method with cls
# staticmethod.__get__ returns the plain function지연 속성 디스크립터
값을 한 번 계산한 뒤 인스턴스별로 캐시하는 디스크립터를 만들어 보세요.
class LazyAttr:
def __init__(self, func): self.func = func
def __set_name__(self, owner, name): self.name = name
def __get__(self, obj, t):
if obj is None: return self
val = self.func(obj)
obj.__dict__[self.name] = val # shadows descriptor
return val
class Report:
@LazyAttr
def summary(self):
print("computing...")
return "done"
r = Report()
print(r.summary) # computing... done
print(r.summary) # done (cached)property는 디스크립터입니다
property 자체가 C로 구현된 데이터 디스크립터입니다. 디스크립터를 이해하면 @property, @classmethod, @staticmethod가 모두 작동하는 방식을 이해하는 데 도움이 됩니다.
class Circle:
def __init__(self, r): self._r = r
@property
def radius(self): return self._r
@radius.setter
def radius(self, v):
if v < 0: raise ValueError
self._r = v
# property is: property.__get__ = fget, property.__set__ = fset디스크립터 조회 순서
파이썬 속성 조회 순서: 1) 타입(MRO)의 데이터 디스크립터, 2) 인스턴스 __dict__, 3) 비데이터 디스크립터와 클래스 변수입니다.
# Pseudocode for obj.attr:
# 1. Check type(obj).__mro__ for a data descriptor
# 2. Check obj.__dict__
# 3. Check type(obj).__mro__ for non-data descriptor or class var
# 4. Raise AttributeError빠른 확인
인스턴스 __dict__보다 우선하는 디스크립터 유형은 무엇입니까?
복습
디스크립터는 __get__, __set__, __delete__를 통해 속성 접근을 가로챕니다. 데이터 디스크립터는 인스턴스 __dict__를 가리지만, 비데이터 디스크립터는 그렇지 않습니다. __set_name__을 사용하면 속성 이름을 알 수 있습니다. property, classmethod, staticmethod는 모두 디스크립터입니다.
자주 묻는 질문
“디스크립터: __get__, __set__, __delete__” 강의는 무료인가요?
네 — “디스크립터: __get__, __set__, __delete__” 전체 내용을 이 웹사이트에서 무료로 읽을 수 있습니다. 인터랙티브하게 실습하려면(내장 코드 에디터와 24/7 AI 튜터), CoddyKit PRO로 업그레이드하면 Python Academy 강의 전체를 잠금 해제할 수 있습니다. Python Academy 강의에는 총 4개의 강의가 포함되어 있습니다.
“디스크립터: __get__, __set__, __delete__”에서 뭘 배우나요?
속성 액세스 제어를 위해 디스크립터 프로토콜을 구현합니다. 브라우저에서 직접 실행하는 실습 코드로 Python Academy을(를) 배우며, 24/7 AI 튜터가 강의를 진행하면서 질문에 답변해줍니다.
Python Academy을(를) 시작하는 데 경험이 필요한가요?
사전 경험은 필요하지 않습니다. CoddyKit의 Python Academy은(는) 초급자부터 고급 학습자까지를 위해 구성되어 있으므로, 여기서 시작하거나 처음부터 시작할 수 있으며 자신의 속도대로 진행할 수 있습니다. 이것은 4개 중 3번째 강의입니다.
“디스크립터: __get__, __set__, __delete__” 강의는 얼마나 걸리나요?
대부분의 CoddyKit 강의는 약 5~10분이 소요됩니다. 각 강의는 간결하고 인터랙티브하여 꾸준한 진행이 가능하며, 웹과 앱에서 중단한 부분부터 바로 시작할 수 있습니다.
이 Python Academy 강의에서 코드를 작성하고 실행할 수 있나요?
네. 모든 Python Academy 강의에는 내장 코드 에디터가 포함되어 있으므로, 브라우저에서 바로 실제 코드를 작성하고 실행한 후 즉시 AI 피드백을 받을 수 있습니다 — 로컬 설정이 필요 없습니다.
이 강의의 모든 강의
- Python 클래스가 생성되는 방식
- 사용자 정의 메타클래스 작성
- 디스크립터: __get__, __set__, __delete__
- __slots__와 메모리 최적화