Атрибуты в Python: экземпляр vs класс (setattr, delattr, getattr)
@python_quizРазберем этот квиз
Короткая задача: что напечатает этот фрагмент кода и почему?

Быстрая суть
При доступе к атрибуту Python сначала проверяет атрибуты экземпляра, затем — атрибуты класса (и далее по иерархии наследования). Команды в примере меняют атрибуты в разных «местах», поэтому итоговый вывод определяется значением атрибута экземпляра.
Пошаговый разбор кода
- class C:
x = 1
- Класс C получает атрибут x со значением 1. Это атрибут класса.
c = C()
- Создаётся экземпляр c. Пока в c.__dict__ нет ключа 'x'.
- setattr(c, 'x', 2)
- Устанавливает атрибут 'x' прямо в экземпляре c (аналогично c.x = 2). Теперь у экземпляра есть собственный атрибут x: c.__dict__ содержит {'x': 2}.
- delattr(C, 'x')
- Удаляет атрибут x из класса C. После этого класс больше не имеет x в своём словаре атрибутов.
- getattr(c, 'x', 10)
- При попытке получить c.x сначала ищется в c.__dict__ — там есть значение 2, поэтому возвращается 2. Параметр по умолчанию 10 не используется.
Итог: программа выведет 2.
Демонстрация и дополнительные проверки
Ниже тот же пример с дополнительными выводами для проверки словарей атрибутов:

Ожидаемый вывод (сокращённо):
- C.
__dict__до: {'x': 1} - c.
__dict__до: {} - После setattr: c.
__dict__содержит {'x': 2} - После delattr: класс больше не имеет 'x'
- И, наконец: getattr(c, 'x', 10) -> 2
Разбор возможных альтернатив и типичных ошибок
- Почему не 1? Значение 1 было в классе, но оно удалено через delattr(C, 'x'). Даже если бы классный атрибут остался, атрибут экземпляра (2) перекрывает значение класса.
- Почему не 10? Параметр по умолчанию у getattr используется только если атрибут вообще не найден ни в экземпляре, ни в классе. Здесь атрибут найден в экземпляре.
- Почему не
AttributeError?AttributeErrorвозник бы, если бы мы удалили атрибут и не передали значение по умолчанию в getattr (или делали бы прямой доступ c.x после удаления обоих мест хранения). В нашем случае getattr получает значение из экземпляра, поэтому исключения нет.
Небольшие подсказки для практики
- Атрибуты экземпляра хранятся в экземпляре (обычно c.
__dict__); они имеют приоритет перед атрибутами класса при чтении. - setattr/ delattr / getattr — это динамические аналоги операций присвоения, удаления и доступа (c.
x = v, del C.x, c.x). - Если нужно удалить именно атрибут экземпляра, вызывайте delattr(c, 'x'); delattr(C, 'x') удалит атрибут у класса.
Вывод
В приведённом примере результат формируется тем, что экземпляру присвоили собственный атрибут со значением 2, а затем удалили атрибут у класса. При чтении атрибута возвращается значение экземпляра — 2.