Приписывайте свой успех 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 года: