> Как реализуются атрибуты в классах Python (Python)

Уровень: senior · Роль: backend · Язык: Python · Категория: Технические вопросы

Компании: CoMagic, TradingView

Стек: Python

> Пример ответа

Короткий ответ

Атрибуты в Python реализуются через пространства имён объектов: каждый объект имеет __dict__ (обычно), где хранятся атрибуты экземпляра. Доступ к атрибуту проходит через цепочку: __getattribute__ → поиск в __dict__ экземпляра → класс → родительские классы → __getattr__ (если не найден). Классовые атрибуты хранятся в __dict__ класса. Дескрипторы (например, property, staticmethod) перехватывают доступ через протокол __get__/__set__.

Подробное объяснение

Механизм атрибутов в Python - это комбинация нескольких уровней:

  1. Хранение: у каждого объекта есть __dict__ (словарь) для атрибутов экземпляра. У классов тоже есть __dict__, но он содержит атрибуты класса (методы, переменные класса).

  2. Поиск атрибута: при обращении obj.attr вызывается type(obj).__getattribute__(obj, 'attr'). Порядок поиска:

    • data descriptor в классе или родителях (если есть __get__ и __set__)
    • obj.__dict__
    • non-data descriptor в классе или родителях
    • __getattr__ (если определён и атрибут не найден)
  3. Дескрипторы: объекты с методами __get__, __set__, __delete__. property - это data descriptor, staticmethod/classmethod - non-data descriptors.

  4. Слоты: __slots__ заменяет __dict__ на более компактное хранение, но ломает динамическое добавление атрибутов.

  5. Метапрограммирование: __setattr__, __getattr__, __delattr__ позволяют перехватывать операции на уровне экземпляра.

На практике

  • Для простых случаев Python автоматически создаёт __dict__ - не нужно ничего реализовывать.
  • Используйте @property для вычисляемых атрибутов и контроля доступа.
  • __slots__ - для экономии памяти при большом количестве экземпляров (например, в data-heavy приложениях).
  • Будьте осторожны с переопределением __getattribute__ - это влияет на все атрибуты и может привести к рекурсии.
  • Для фреймворков (ORM, сериализация) часто используют __getattr__ для ленивой загрузки или виртуальных атрибутов.

Пример кода

PYTHON
class User:
def __init__(self, name):
self.name = name # обычный атрибут
@property
def upper_name(self):
return self.name.upper()
def __getattr__(self, item):
# вызывается только если атрибут не найден обычным способом
if item == 'age':
return 30
raise AttributeError(item)
u = User('alice')
print(u.name) # 'alice'
print(u.upper_name) # 'ALICE'
print(u.age) # 30 (через __getattr__)
print(u.__dict__) # {'name': 'alice'}

Как отвечать на собеседовании

Начните с простого: "атрибуты - это пары ключ-значение в __dict__ объекта". Затем покажите понимание цепочки поиска: экземпляр → класс → родители. Упомяните дескрипторы как ключевой механизм для property и методов. Если спросят про производительность - скажите про __slots__. Для senior важно показать, что вы понимаете разницу между __getattr__ и __getattribute__, и когда что использовать.

Что проверяет интервьюер

  • Понимание модели данных Python (data model).
  • Знание порядка разрешения атрибутов (MRO, descriptors).
  • Умение объяснить, почему property работает именно так.
  • Понимание trade-off между гибкостью (__dict__) и производительностью (__slots__).
  • Способность применить это в реальных задачах (ORM, сериализация, кэширование).

Типичные ошибки

  • Путаница между __getattr__ и __getattribute__ - первая вызывается только при отсутствии атрибута, вторая - всегда.
  • Забывают, что __slots__ не наследуется автоматически - нужно указывать в каждом классе.
  • Переопределяют __getattribute__ без вызова super().__getattribute__ - получают бесконечную рекурсию.
  • Думают, что obj.__dict__ - это единственное место хранения атрибутов, забывая про класс.
  • Используют property для простых атрибутов без логики - это лишняя сложность.

> ГОТОВЫ К СЛЕДУЮЩЕМУ СОБЕСЕДОВАНИЮ?

Запустите тренировочную сессию с ИИ и получите детальную обратную связь, чтобы увереннее проходить реальные интервью