Вопрос с собеседования · Python

Чем __getattr__ отличается от __getattribute__?

MiddleООП
Ещё спрашивают так:
  • Как перехватить обращение к несуществующему атрибуту?
Коротко

__getattribute__ вызывается при каждом обращении к атрибуту — это сам механизм поиска. __getattr__ вызывается только если атрибут не найден обычным путём. Для прокси, ленивых атрибутов и обёрток используют __getattr__ — он безопаснее и дешевле.

  • В __getattribute__ легко получить бесконечную рекурсию — обращаться через object.__getattribute__(self, name)
  • __getattr__ должен бросать AttributeError для неизвестных имён
  • Модуль тоже может иметь __getattr__ (PEP 562) — ленивый импорт, предупреждения об устаревании

А ещё Пара для записи — __setattr__ и __delattr__

Подробно

__getattribute__ — точка входа всякого obj.name. Реализация по умолчанию (object.__getattribute__) выполняет поиск: дескрипторы данных, словарь экземпляра, атрибуты класса. Переопределяют редко — для отладки, контроля доступа. Любое self.x внутри снова вызывает __getattribute__ — рекурсия; обращаются через super() или object.__getattribute__(self, name).

__getattr__ — запасной путь: вызывается, когда обычный поиск бросил AttributeError. Подходит для:

  • прокси и обёрток — делегировать всё неизвестное обёрнутому объекту;
  • динамических атрибутов — API-клиент, где client.users строит запрос;
  • ленивой загрузки.

Важно бросать AttributeError для неизвестных имён: иначе ломаются hasattr, getattr с умолчанием, pickle и copy.

Модульный __getattr__ (PEP 562): функция __getattr__(name) на уровне модуля вызывается для отсутствующих имён — ленивый импорт тяжёлых подмодулей, предупреждение об устаревших именах.

Пример

class Proxy:
    def __init__(self, target):
        self._target = target
    def __getattr__(self, name):          # только для отсутствующих
        print("доступ к", name)
        return getattr(self._target, name)

Что хотят услышать

  • Когда вызывается каждый.
  • Риск рекурсии.
  • Типичные применения.

Проверьте себя

Один верный вариант. Подумайте, потом откройте ответ.

Показать варианты и ответ
  • ✓__getattribute__ — при каждом обращении, __getattr__ — если атрибут не найден
  • ✗__getattr__ вызывается при каждом обращении, а __getattribute__ — только если атрибут не найден
  • ✗__getattribute__ — безопасный выбор для прокси-объектов: в нём невозможно уйти в рекурсию
  • ✗__getattr__ должен возвращать None, если атрибута нет, а не бросать AttributeError

Разбор темы

Связанные вопросы

Партнёр платформы

Разбор собеседования с sotto

После собеседования sotto разбирает разговор: оценка, каждый вопрос — хорошо, частично или слабо, чего не хватило в ответе и над чем поработать к следующему разу. Первые 30 минут бесплатно.

Разбор собеседования