ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [Python] 디스크립터(descriptor)를 이해한다는 것
    IT 2026. 1. 22. 17:29

     

    코딩을 하면서 이런 코드는 한번씩 본 적이 있을 겁니다.

    class User:
        def __init__(self, name):
            self.name = name
    
    u = User("Alice")
    print(u.name)

    u.name이라는 표현은 너무 자연스럽습니다. 하지만 여기서 한 발짝만 물러서서 생각해 보면, 사실 이 문법은 꽤 많은 일을 한꺼번에 처리하고 있습니다.

    "객체의 속성에 접근한다는 것은, 정확히 무엇을 의미할까?"

    디스크립터(descriptor)는 바로 이 질문에 대한 파이썬의 깊은 대답입니다. 이 글에서는 디스크립터를 낯선 특수 메서드의 묶음으로 설명하기 보다는, 파이썬이 속성 접근을 어떻게 설계했는지 라는 흐름 속에서 풀어보려 합니다.

     

    속성 접근은 생각보다 단순하지 않다

    파이썬에서 점(.)을 찍어 속성에 접근하면, 단순히 딕셔너리에서 값을 꺼내는 것처럼 보입니다.

    하지만 실제로 파이썬은 다음과 같은 순서를 따릅니다.

     

    클래스에서 디스크립터가 있는 지 확인 -> 없다면 인스턴스의 __dict__를 확인 -> 그래도 없다면 클래스 속성을 찾음

     

    이 과정에서 중요한 역할을 하는 것은 바로 디스크립터입니다. 즉, 디스크립터는 속성 접근 과정에 개입할 수 있는 공식적인 훅(hook) 입니다.

     

    디스크립터란 무엇인가

    디스크립터는 아주 간단한 약속에서 출발합니다.

    어떤 객체가 속성 접근에 관여하고 싶다면, 정해진 메서드를 구현하라

    그 메서들은 아래와 같습니다.

     

    • __get__(self, instance, owner)
    • __set__(self, instance, value)
    • __delete__(self, instance)

    이 중 하나 이상을 구현한 객체를, 파이썬은 디스크립터로 취급합니다.

     

    디스크립터가 실제로 하는 일

    말로만 들으면 감이 잘 오지 않기 때문에, 아주 간단한 예제를 하나 살펴보겠습니다.

    class UpperCase:
        def __get__(self, instance, owner):
            if instance is None:
                return self	
        return instance.__dict__["name"].upper()
    
    
    def __set__(self, instance, value):
        instance.__dict__["name"] = value
    
    
    class User:
        name = UpperCase()
    
    
    u = User()
    u.name = "alice"
    print(u.name)

    여기서 핵심은 name이 더 이상 단순한 문자열이 아니라는 점 입니다. name은 속성 접근을 가로채는 객체가 되었고, 그 결과 접근 시마다 값이 가공됩니다.

    이처럼 디스크립터는 속성 자체를 데이터가 아니라 행동을 가진 객체로 바꿔 줍니다.

     

    왜 이런 복잡한 구조가 필요했을까?

    이쯤에서 자연스러운 질문이 떠오릅니다.

    "굳이 이렇게까지 해서 얻는 이점이 뭘까?"

    디스크립터의 진짜 가치는, 공통 로직을 속성 단위로 분리할 수 있다는 데 있습니다.

    • 타입 검증
    • 값의 범위 제한
    • 지연 계산
    • 접근 로깅

    이런 로직들을 매번 메서드로 구현하지 않고, 속성 자체의 책임으로 옮길 수 있습니다.

     

    디스크립터와 property의 관계

    여기까지 읽었다면, property가 떠오를지도 모릅니다.

    class User:
        @property
            def name(self):
                return self._name

    사실 property는 디스크립터를 감싼 아주 얇은 추상화입니다. 내부적으로는 __get__, __set__을 구현한 객체일 뿐입니다.

    즉, 우리가 일상적으로 사용하는 @property 문법은, 디스크립터를 좀 더 안전하고 읽기 쉽게 포장한 결과물입니다.

     

    데이터 디스크립터와 논데이터 디스크립터

    디스크립터는 구현된 메서드에 따라 두 가지로 나뉩니다.

    • 데이터 디스크립터: __get____set__을 모두 가짐
    • 논데이터 디스크립터: __get__만 가짐

    이 차이는 속성 탐색 우선순위에 영향을 줍니다. 데이터 디스크립터는 인스턴스 딕셔너리보다 항상 우선하며, 논데이터 디스크립터는 그렇지 않습니다.

    이 규칙 덕분에 파이썬은 속성 접근에 대해 일관된 기준을 유지할 수 있습니다

     

    디스크립터는 파이썬 철학의 연장선이다

    지금까지 살펴본 개념들을 떠올려보면, 디스크립터는 낯선 존재가 아닙니다.

    • 클로저 → 행동과 상태를 묶고
    • 데코레이터 → 기존 구조를 감싸며
    • 시퀀스 프로토콜 → 행동으로 객체를 판단하고

    디스크립터 역시 같은 방향에 있습니다. 파이썬은 속성조차도 행동 중심으로 재정의할 수 있게 만들어 둔 언어입니다.

     

    마무리하며

    디스크립터는 처음에는 과하고 복잡해 보일 수 있습니다. 하지만 이 개념을 이해하는 순간, 파이썬의 많은 기능들이 하나의 그림으로 이어지기 시작합니다.

    "파이썬에서 점(.) 하나는 단순한 접근 연산자가 아니다"

    그 안에는 객체 모델, 책임 분리, 그리고 유연한 설계 철학이 함께 들어 있습니다. 디스크립터는 그 철학이 가장 깊게 드러나는 지점 중 하나입니다.

     

    개념 설명이 거의 다 끝나갑니다.

    다음 블로그에서 뵙겠습니다.

     

Designed by Tistory.