Атрибуты класса и экземпляра в Python: setattr, delattr, getattr
@python_quizРазберем этот квиз
Коротко: разберём, почему при последовательности операций с атрибутами выводится именно то значение, которое вы видите в примере.
Исходный код

Пошаговый разбор
- class C:
x = 1
- Здесь x — атрибут класса. У класса C и у всех его экземпляров по умолчанию доступно значение x = 1, пока у экземпляра нет собственного атрибута x.
obj = C()
- Создаём экземпляр obj. На данный момент у obj нет собственного атрибута x, но при обращении к obj.x произойдёт поиск в атрибутах экземпляра, затем в атрибутах класса — вернётся 1.
- setattr(obj, 'x', 10)
- setattr на объекте-экземпляре создаёт (или изменяет) атрибут именно этого экземпляра. После этой строки obj.__dict__ содержит 'x': 10. Это не трогает атрибут класса C.x.
- delattr(C, 'x')
- Удаляет атрибут x из класса C. Теперь у класса C больше нет x. Но у экземпляра obj всё ещё есть собственный атрибут x = 10.
- getattr(obj, 'x', 99)
- При чтении obj.x сначала смотрят в словаре экземпляра — там найдено 10, поэтому возвращается 10. Значение по умолчанию 99 не используется.
Итог: программа выведет

Почему не другие варианты
- "1": это значение атрибута класса изначально, но после
setattr(obj, 'x', 10)у экземпляра появляется свойx, поэтому обращение кobj.xвозвращает значение экземпляра, а не класса. Даже после удаленияC.xэкземпляр сохраняет своё значение.
- "99": это запасное значение, которое возвращается только если атрибута нет ни в экземпляре, ни в классе. В нашем случае атрибут у экземпляра есть, значит запасной вариант не применяется.
- "
AttributeError": такое возникло бы, если бы использовалсяobj.xпри отсутствии атрибута и без использованияgetattrс запасным значением, либо если бы удалили атрибут экземпляра и класса и попытались обратиться без default. В приведённом коде используетсяgetattrс запасным значением, и у экземпляра есть атрибут, так что исключения не будет.
Дополнительные примеры
1) Если удалить атрибут экземпляра — результат будет другим:

Вывод:

Пояснение: после удаления у экземпляра нет собственного x, поэтому используется значение класса C.x.
2) Если удалить и у экземпляра, и у класса:

Вывод:

Пояснение: ни у экземпляра, ни у класса нет x, возвращается значение по умолчанию.
3) Пример с исключением:

В этом случае будет:

потому что атрибута нет ни у экземпляра, ни у класса, и мы не использовали getattr с запасным значением.
Выводы
- setattr(obj, 'name', value) создаёт или меняет атрибут именно экземпляра, если первый аргумент — экземпляр.
- delattr(C, 'name') удаляет атрибут из класса; это не затрагивает уже существующие атрибуты в экземплярах.
- При чтении атрибута Python сначала ищет его в экземпляре, затем в классе, потом в базовых классах; только если нигде не найдено, применяется значение по умолчанию в getattr или бросается
AttributeErrorпри обычном доступе.
Понимание этой последовательности важно для работы с наследованием, свойствами и динамическим изменением объектов в Python.