파이썬 디스크립터란?
파이썬에서 디스크립터는 클래스를 통해 속성(attribute) 접근을 제어하기 위한 프로토콜(Protocol)입니다. 디스크립터는 클래스 내에 get, set, delete 메소드를 구현하여, 속성의 값을 읽거나 쓰거나 삭제하기 전에 추가적인 로직을 수행할 수 있도록 합니다.
디스크립터의 종류
파이썬에서는 디스크립터를 세 가지 종류로 구분합니다. 각각은 다음과 같습니다.
- 데이터 디스크립터(Data Descriptor)
- 비데이터 디스크립터(Non-Data Descriptor)
- 클래스 디스크립터(Class Descriptor)
1. 데이터 디스크립터(Data Descriptor)
데이터 디스크립터는 __get__과 set 메소드를 모두 구현한 디스크립터입니다. 이 디스크립터를 사용하면 속성의 값을 읽거나 쓸 때마다 __get__과 set 메소드에 정의된 로직이 실행됩니다. 다음은 데이터 디스크립터를 사용하는 예시입니다.
class Descriptor:
def __get__(self, instance, owner):
print("속성의 값을 가져옵니다.")
return instance.__dict__[self.name]
def __set__(self, instance, value):
print("속성의 값을 설정합니다.")
instance.__dict__[self.name] = value
class MyClass:
x = Descriptor()
위 코드에서 Descriptor 클래스는 __get__과 set 메소드를 모두 구현하고 있으므로, 데이터 디스크립터입니다. MyClass 클래스 내에 x라는 속성을 정의할 때, Descriptor 클래스를 이용하여 x의 값을 읽고 쓸 수 있습니다.
2. 비데이터 디스크립터(Non-Data Descriptor)
비데이터 디스크립터는 get 메소드만을 구현한 디스크립터입니다. 이 디스크립터를 사용하면 속성의 값을 읽을 때마다 get 메소드에 정의된 로직이 실행됩니다. 하지만 속성의 값을 쓰거나 삭제할 때는 일반적인 속성(attribute)으로 처리됩니다. 다음은 비데이터 디스크립터를 사용하는 예시입니다.
class Descriptor:
def __get__(self, instance, owner):
print("속성의 값을 가져옵니다.")
return instance.__dict__[self.name]
class MyClass:
x = Descriptor()
위 코드에서 Descriptor 클래스는 get 메소드만을 구현하고 있으므로, 비데이터 디스크립터입니다. MyClass 클래스 내에 x라는 속성을 정의할 때, Descriptor 클래스를 이용하여 x의 값을 읽을 수 있습니다.
3. 클래스 디스크립터(Class Descriptor)
클래스 디스크립터는 클래스 레벨에서 디스크립터를 정의하고, 해당 클래스의 모든 인스턴스에서 공유하는 디스크립터입니다. 클래스 디스크립터를 사용하면 해당 클래스의 모든 인스턴스에서 속성의 값을 읽거나 쓸 때마다, 클래스 레벨에서 정의된 __get__과 set 메소드가 실행됩니다. 다음은 클래스 디스크립터를 사용하는 예시입니다.
class Descriptor:
def __get__(self, instance, owner):
print("속성의 값을 가져옵니다.")
return instance.__dict__[self.name]
def __set__(self, instance, value):
print("속성의 값을 설정합니다.")
instance.__dict__[self.name] = value
class MyClass:
x = Descriptor()
class MyOtherClass(MyClass):
pass
obj1 = MyClass()
obj2 = MyOtherClass()
obj1.x = 10
obj2.x = 20
print(obj1.x)
print(obj2.x)
위 코드에서 Descriptor 클래스는 __get__과 set 메소드를 모두 구현하고 있으므로, 데이터 디스크립터입니다. MyClass 클래스 내에 x라는 속성을 정의할 때, Descriptor 클래스를 이용하여 x의 값을 읽고 쓸 수 있습니다. MyOtherClass 클래스는 MyClass를 상속받기 때문에, MyClass에 정의된 x 속성을 그대로 사용할 수 있습니다. obj1과 obj2는 서로 다른 인스턴스이므로, 각각의 x 속성 값을 따로 설정할 수 있습니다. 하지만 MyClass와 MyOtherClass는 같은 클래스 디스크립터 x를 공유하므로, x의 값을 읽거나 쓸 때마다 Descriptor 클래스의 __get__과 set 메소드가 실행됩니다.
디스크립터 사용 예시
디스크립터는 클래스의 속성 접근을 제어하기 때문에, 클래스를 통해 다양한 기능을 구현할 때 유용하게 사용됩니다. 다음은 디스크립터를 사용한 예시 중 하나입니다.
class PositiveNumber:
def __init__(self, value):
self.value = value
def __get__(self, instance, owner):
return self.value
def __set__(self, instance, value):
if value < 0:
raise ValueError("양수만 입력 가능합니다.")
self.value = value
class Rectangle:
width = PositiveNumber(0)
height = PositiveNumber(0)
def __init__(self, width, height):
self.width = width
self.height = height
def area(self):
return self.width * self.height
위 코드에서 PositiveNumber 클래스는 __get__과 set 메소드를 모두 구현하고 있으므로, 데이터 디스크립터입니다. Rectangle 클래스 내에 width와 height라는 속성을 정의할 때, PositiveNumber 클래스를 이용하여 양수만 입력 가능하도록 설정할 수 있습니다. 이를 이용하여, Rectangle 인스턴스의 너비와 높이가 양수인지 확인하고, 넓이를 계산할 수 있습니다.
결론
이상으로 파이썬 디스크립터에 대해 자세히 알아보았습니다. 디스크립터는 클래스를 통해 속성의 값을 제어하고 추가적인 로직을 수행할 수 있도록 해주므로, 파이썬 프로그래밍에서 다양하게 활용될 수 있습니다.