Дескрипторы и магия атрибутов
Возьмите под контроль то, что на самом деле делает 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 следует строгому порядку. Понимание этого порядка -- фундамент всей темы.
- Python идёт по MRO типа (порядок разрешения методов) в поисках
attr. Если найден атрибут класса, являющийся data-дескриптором (определяет__set__или__delete__), этот дескриптор побеждает сразу и запускается его__get__. - Иначе Python проверяет собственный
__dict__экземпляра. Еслиattrтам есть, возвращается это значение. - Иначе -- обратно к атрибуту класса, найденному в MRO: если это non-data-дескриптор (только
__get__) или обычное значение, используется он. - Если ничего не найдено, Python вызывает
__getattr__как крайнюю меру. Если и его нет, возбуждаетсяAttributeError.
Самое важное следствие: data-дескриптор на классе бьёт словарь экземпляра, а non-data-дескриптор проигрывает словарю экземпляра. Одно это правило объясняет большинство сюрпризов "почему мой атрибут повёл себя так?".