Перейти к содержанию

Дескрипторы и магия атрибутов

Начало урока

Дескрипторы и магия атрибутов

Возьмите под контроль то, что на самом деле делает obj.attr

Главная идея: Каждый раз, когда вы пишете obj.attr, Python выполняет небольшой алгоритм, решая, откуда взять это значение. Три механизма позволяют изменить этот алгоритм: протокол дескрипторов (__get__/__set__/__delete__) привязывает переиспользуемую логику чтения/записи к атрибуту класса; __getattr__ и __getattribute__ перехватывают обращения к атрибутам экземпляра; а __slots__ заменяет __dict__ каждого экземпляра фиксированной компактной раскладкой. Освойте их -- и вы поймёте, как устроены property, classmethod, ORM и dataclasses.
К концу урока вы сможете:
  • проследить точный порядок, в котором Python разрешает obj.attr,
  • написать переиспользуемый дескриптор с __get__, __set__ и __set_name__,
  • отличать data-дескрипторы от non-data и предсказывать, кто побеждает словарь экземпляра,
  • использовать __getattr__ для запасных значений и понимать, почему __getattribute__ -- заряженное ружьё,
  • применять __slots__ для экономии памяти и фиксации набора атрибутов, зная компромиссы.

1. Что на самом деле происходит при obj.attr

Доступ к атрибуту -- это не простой поиск в словаре. Чтение obj.attr вызывает type(obj).__getattribute__(obj, "attr"), и реализация по умолчанию в object следует строгому порядку. Понимание этого порядка -- фундамент всей темы.

  1. Python идёт по MRO типа (порядок разрешения методов) в поисках attr. Если найден атрибут класса, являющийся data-дескриптором (определяет __set__ или __delete__), этот дескриптор побеждает сразу и запускается его __get__.
  2. Иначе Python проверяет собственный __dict__ экземпляра. Если attr там есть, возвращается это значение.
  3. Иначе -- обратно к атрибуту класса, найденному в MRO: если это non-data-дескриптор (только __get__) или обычное значение, используется он.
  4. Если ничего не найдено, Python вызывает __getattr__ как крайнюю меру. Если и его нет, возбуждается AttributeError.

Самое важное следствие: data-дескриптор на классе бьёт словарь экземпляра, а non-data-дескриптор проигрывает словарю экземпляра. Одно это правило объясняет большинство сюрпризов "почему мой атрибут повёл себя так?".

Продолжите этот урок на YourAIZone

В полном уроке — объяснение целиком, разобранные примеры, частые ошибки, тесты с мгновенной проверкой и ИИ-репетитор. Начните сразу как гость или создайте аккаунт, чтобы сохранять прогресс.

В полном уроке

  • Объяснение целиком
  • Разобранные примеры
  • Частые ошибки и как их избежать
  • Тестовые вопросы: 15
  • ИИ-репетитор, который отвечает на вопросы по теме
  • Отслеживание прогресса для ученика, родителей и учителя