descriptor란 __get__나 __set__, __delete__ 등의 magic method가 구현되어 있는 객체를 의미한다. descriptor는 다른 객체의 속성으로써 존재할 때, 그 속성에 대하여 읽기, 쓰기, 삭제 등의 동작을 할때, 동작에 따라 각 구현된 method가 호출되는 객체이다.
object a가 있고 attribute로 b라는게 있다고 하면. a.b로 b를 조회할 수 있음 ㅇㅇ.
이게 일반적인 상황이면 a의 dict에서(물론 __slots__가 구현되어 있으면 딕셔너리 아님) b를 찾아서 조회하는데 만약 b가 descriptor object면 b의 __get__이 실행되는 것임
그 classmethod나 staticmethod, property 등을 구현할 때 쓰임.
알아두면 python에 대한 insight가 조금 증가하는 느낌이 든다. ㅎㅎ
descriptor는 3가지 프로토콜이 있다. 이미 위에서 설명한 dunder가 붙은 get, set, delete이다. 이들 중 어떤 것이 구현되어 있느냐에 따라서 다른 용도로 쓰인다.
만약 get만 구현 시, 비 데이터 디스크립터이고.
set과 delete를 구현시 데이터 디스크립터이다.
간단한 property와 비슷한 동작을 하는 디스크립터를 구현해보자!
class MyProperty():
def __init__(self, getter=None, setter=None, deleter=None):
self.getter = getter
self.setter = setter
self.deleter = deleter
def __get__(self, instance, owner=None):
if self.getter is None:
raise AttributeError
print('get egg')
return self.getter(instance)
def __set__(self, instance, value):
if self.setter is None:
raise AttributeError
print(f'set egg to {value}')
return self.setter(instance, value)
def __delete__(self, instance):
if self.deleter is None:
raise AttributeError
return self.deleter(instance)
생성 시에 getter, setter, deleter를 인자로 받고 저장한 뒤, 각각 동작에 따라 init때 받은 함수를 호출해주는 디스크립터를 구현했따.
def __init__(self):
self.egg = 0
def get_egg(self):
return self._egg
def set_egg(self, value):
self._egg = value
def delete_egg(self):
del(self._egg)
egg = MyProperty(getter=get_egg, setter=set_egg, deleter=delete_egg)
위와 같이 다른 한 class의 속성으로써 디스크립터 객체를 놓고. 아래의 코드를 실행하면
my_egg = Egg()
print(my_egg.egg)
my_egg.egg = 10
print(my_egg.egg)
아래와 같이 디스크립터에 선언한 메소드가 실행되었음을 알 수 있다.
set egg to 0
get egg
0
set egg to 10
get egg
10
그니까 디스크립터는 어... 속성의 읽기 쓰기 삭제를 개발자가 커스텀할 수 있게 해주는 객체...?