
Приписывайте свой успех Python
Головоломка Python

Как вы думаете, что напечатает следующая программа?
Эта программа завершится ошибкой: RecursionError: maximum recursion depth exceeded.
Поиск атрибутов Python, который происходит каждый раз, когда вы, например, пишете now.year, довольно сложен. У вас как у разработчика есть множество возможностей изменить способ поиска атрибутов в ваших классах: свойства, искаженные имена (например, __name), staticmethod, classmethod, __slots__, дескрипторы и многое другое.
Вы также можете реализовать два специальных метода для динамического вычисления атрибутов, это __getattribute__ и __getattr__. Вы захотите использовать __getattr__ :)
Смотрим документацию:
__getattr__: вызывается при сбое доступа к атрибуту по умолчанию с ошибкой AttributeError…__getattribute__: вызывается безоговорочно для реализации доступа к атрибутам для экземпляров класса. Если класс также определяет__getattr__, то последний вызываться не будет...
Таким образом, согласно документации, __getattr__ вызывается только тогда, когда обычный поиск атрибутов терпит неудачу. В нашем случае запись new._obj ничего не напечатает, но запись now.year запустит наш метод __getattr__ (поскольку now не имеет атрибута year) и напечатает.
С другой стороны, __getattribute__ полностью обходит обычный поиск атрибутов. Строка val = getattr(self._obj, attr, None) будет вызывать нашу __getattribute__ снова и снова, пока мы не достигнем встроенного предела рекурсии Python.
Исправить легко — переключитесь на __getattr__:
Который будет печатать:
[DEBUG] year -> 2022 2022
Я не могу вспомнить ни одного раза за двадцать пять лет написания кода на Python, чтобы я использовал __getattribute__. Я использовал __getattr__ несколько раз, например, при написании прокси.)
🎓 Дальнейшее изучение. Я рекомендую посмотреть видео Рэймонда Хеттингера Инструменты для разработки классов. Это даст вам хорошее представление о том, когда использовать некоторые из упомянутых инструментов. Или, если вы хотите спуститься в кроличью нору кода C, начните с PyObject_GetAttr в object.c и следуйте коду.
Если вам нравится решать задачи по программированию, ознакомьтесь с книгами Мики Тебека «Дразнилки для ума» на The Pragmatic Bookshelf. Вы можете сэкономить 35% на электронных версиях книг с промокодом brain_teasers_35 до 15 сентября 2022 года: