Python Schoolby trefilov
Шаг 1 из 4+150 XP

Дескрипторы

__get__/__set__ — основа property, methods, ORM полей.

Дескриптор — объект-посредник для атрибута

Дескриптор — это любой объект с методами __get__, __set__ (или __delete__), помещённый в класс как атрибут. Когда ты обращаешься к этому атрибуту через экземпляр, Python вызывает соответствующий метод дескриптора. Так работают property, методы, classmethod, staticmethod, поля ORM (Django, SQLAlchemy) — это всё дескрипторы под капотом.

Как читается

class Positive:
    def __set_name__(self, owner, name):
        self.name = name                 # запоминаем имя атрибута
    def __get__(self, inst, owner):
        return inst.__dict__[self.name]
    def __set__(self, inst, value):
        if value <= 0:
            raise ValueError("must be > 0")
        inst.__dict__[self.name] = value

class Box:
width = Positive()

__set_name__ (3.6+) даёт дескриптору знать своё имя — это позволяет хранить значение в inst.__dict__[name] и иметь по одному значению на экземпляр.

Пример: lazy-атрибут с кешем

class cached_property:
    def __init__(self, fn): self.fn = fn
    def __set_name__(self, o, n): self.name = n
    def __get__(self, inst, owner):
        if inst is None: return self
        v = self.fn(inst)
        inst.__dict__[self.name] = v   # перекрывает дескриптор
        return v

class Report:
@cached_property
def heavy(self):
print("compute")
return 42

r = Report()
r.heavy # compute → 42
r.heavy # 42 (из __dict__, без вычисления)

Когда использовать / подводные камни

👍 Валидация полей, ленивые вычисления, прозрачные обёртки (логгирование доступа), поля ORM/dataclass-аналогов.
👎 Не клади значение в сам дескриптор (self.value = ...) — он один на класс, и все экземпляры начнут делить его. Всегда храни в inst.__dict__ или в WeakKeyDictionary. Для простых случаев @property короче и читаемее.