Вопрос с собеседования · Python

Что такое дескриптор и как на нём построены property и методы?

MiddleООП
Ещё спрашивают так:
  • Как работает @property внутри?
  • Чем data descriptor отличается от non-data descriptor?
Коротко

Дескриптор — объект в атрибуте класса, у которого есть __get__ (и, возможно, __set__, __delete__). При обращении obj.attr Python вызывает эти методы вместо простого чтения. На дескрипторах построены property, методы (функции — дескрипторы), staticmethod, classmethod, __slots__.

  • Data descriptor (есть __set__ или __delete__) важнее словаря экземпляра
  • Non-data (только __get__) уступает словарю экземпляра — так методы можно перекрыть
  • __set_name__ сообщает дескриптору имя атрибута

А ещё Применение: валидация полей, ленивые атрибуты, ORM-поля (Django, SQLAlchemy)

Подробно

Порядок поиска obj.x:

  1. data-дескриптор в классе (и его предках) — вызвать его __get__;
  2. obj.__dict__["x"];
  3. non-data дескриптор или обычный атрибут класса.

Свой дескриптор — валидация:

class Positive:
    def __set_name__(self, owner, name):
        self.name = "_" + name
    def __get__(self, obj, objtype=None):
        return getattr(obj, self.name)
    def __set__(self, obj, value):
        if value <= 0:
            raise ValueError("должно быть > 0")
        setattr(obj, self.name, value)

class Order:
    qty = Positive()
    def __init__(self, qty): self.qty = qty

Методы. Функция в классе — non-data дескриптор: obj.method через __get__ возвращает связанный метод, подставляющий obj как self. staticmethod возвращает функцию без связывания, classmethod — связывает с классом.

property — data-дескриптор, вызывающий заданные геттер, сеттер, делитер.

Ленивое вычисление: functools.cached_property — non-data дескриптор: первый вызов кладёт результат в __dict__ экземпляра, и дальше словарь перекрывает дескриптор.

Что хотят услышать

  • Протокол __get__, __set__, __delete__.
  • Data и non-data, порядок поиска.
  • Что на них построено.

Проверьте себя

Один верный вариант. Подумайте, потом откройте ответ.

Показать варианты и ответ
  • ✗Объект в атрибуте экземпляра с методом __call__; non-data дескриптор важнее словаря экземпляра
  • ✗property реализован без дескрипторов, на уровне интерпретатора
  • ✓Объект в атрибуте класса с __get__ (и, возможно, __set__); на них построены property и методы
  • ✗Дескриптором может быть только функция, объявленная через def

Что спросят дальше

Разбор темы

Связанные вопросы

Партнёр платформы

Разбор собеседования с sotto

После собеседования sotto разбирает разговор: оценка, каждый вопрос — хорошо, частично или слабо, чего не хватило в ответе и над чем поработать к следующему разу. Первые 30 минут бесплатно.

Разбор собеседования