Что такое дескрипторы?

Дескриптор в Python — это объект, который перехватывает доступ к атрибуту и сам решает, что делать при чтении, записи или удалении этого атрибута.

Подробный ответ

Технически дескриптор — любой объект, у которого определён хотя бы один из методов протокола дескрипторов:

__get__(self, instance, owner) — вызывается при чтении obj.attr;

__set__(self, instance, value) — при присваивании obj.attr = value;

__delete__(self, instance) — при del obj.attr.

Когда такой объект лежит как атрибут класса, доступ к этому имени (obj.attr) идёт через методы дескриптора, а не напрямую к __dict__ экземпляра.

Формулировка для собеседования

Дескрипторы — это объекты с методами __get__, __set__, __delete__, которые управляют тем, как читаются, записываются и удаляются атрибуты у других объектов. В коде это выглядит следующим образом:

class LoggedAttribute:
   def __init__(self, name: str):
       self.name = name
       
   def __get__(self, instance, owner):
       if instance is None:
           return self
       value = instance.__dict__.get(self.name)
       print(f"GET {self.name} -> {value}")
       return value
       
   def __set__(self, instance, value):
       print(f"SET {self.name} = {value}")
       instance.__dict__[self.name] = value

class User:
   age = LoggedAttribute("age")  # дескриптор – атрибут класса

u = User()
u.age = 30      # вызывает LoggedAttribute.__set__(...)
print(u.age)    # вызывает LoggedAttribute.__get__(...)

Здесь age — дескриптор, который логирует чтение/запись, когда ты обращаешься к u.age.

Зачем они нужны?

На дескрипторах внутри Python построены:

@property — это частный случай дескриптора c __get__/__set__;

методы класса — функции тоже являются дескрипторами и при доступе через экземпляр превращаются в «связанный метод» с self;

@classmethod, @staticmethod — тоже реализованы через дескрипторы.

Пользовательские дескрипторы используют для:

  1. валидации и тип-проверки полей (например, PositiveInt, EmailStr и т.п.);

  2. ленивых вычислений и кеширования (LazyProperty, кэширование результата первого вычисления);

  3. логирования, трассировки, контроля доступа.

Data и non-data дескрипторы

Есть два важных подтипа дескрипторов

Data descriptor — у дескриптора есть __set__ или __delete__ (обычно оба). Такие дескрипторы имеют приоритет над instance.__dict__.

Non-data descriptor — у дескриптора только __get__. В этом случае, если в instance.__dict__ есть одноимённый атрибут, он перебивает дескриптор.

Это объясняет, почему property - это «жёсткое» поле: оно всегда контролирует и чтение, и запись, и не даёт тебе просто так сохранить поверх него другое значение в экземпляре. Любая попытка obj.prop = ... идёт через его __set__.

Обычный метод — «мягкий» дескриптор: он управляет только чтением. Если ты один раз сделал obj.method = 123, ты просто положил значение в obj.__dict__ и тем самым «перекрыл» метод для этого конкретного объекта.

Дескрипторы в Python — это механизм, который позволяет вынести логику доступа к атрибутам в отдельные объекты. Любой объект с __get__, __set__, __delete__, положенный как атрибут класса, становится дескриптором и управляет тем, что происходит при чтении, записи и удалении этого атрибута. На дескрипторах реализованы property, методы, classmethod и staticmethod. На практике их применяют для переиспользуемых «умных» полей: валидация, логирование, lazy‑свойства и т.д.

Оцени свой прогресс

Честно оцени своё понимание этого вопроса, чтобы мы могли построить твой учебный трек максимально эффективно.
Читать в блоге