파이썬/Basic

파이썬 디스크립터란?

코샵 2023. 6. 28. 13:04
반응형

파이썬에서 디스크립터는 클래스를 통해 속성(attribute) 접근을 제어하기 위한 프로토콜(Protocol)입니다. 디스크립터는 클래스 내에 get, set, delete 메소드를 구현하여, 속성의 값을 읽거나 쓰거나 삭제하기 전에 추가적인 로직을 수행할 수 있도록 합니다.

디스크립터의 종류

파이썬에서는 디스크립터를 세 가지 종류로 구분합니다. 각각은 다음과 같습니다.

  1. 데이터 디스크립터(Data Descriptor)
  2. 비데이터 디스크립터(Non-Data Descriptor)
  3. 클래스 디스크립터(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 인스턴스의 너비와 높이가 양수인지 확인하고, 넓이를 계산할 수 있습니다.

결론

이상으로 파이썬 디스크립터에 대해 자세히 알아보았습니다. 디스크립터는 클래스를 통해 속성의 값을 제어하고 추가적인 로직을 수행할 수 있도록 해주므로, 파이썬 프로그래밍에서 다양하게 활용될 수 있습니다.