Атрибуты класса и экземпляра в Python: setattr, delattr, getattr

Атрибуты класса и экземпляра в Python: setattr, delattr, getattr

@python_quiz

Разберем этот квиз

Коротко: разберём, почему при последовательности операций с атрибутами выводится именно то значение, которое вы видите в примере.

Исходный код

python

Пошаговый разбор

  1. class C: x = 1

- Здесь x — атрибут класса. У класса C и у всех его экземпляров по умолчанию доступно значение x = 1, пока у экземпляра нет собственного атрибута x.

  1. obj = C()

- Создаём экземпляр obj. На данный момент у obj нет собственного атрибута x, но при обращении к obj.x произойдёт поиск в атрибутах экземпляра, затем в атрибутах класса — вернётся 1.

  1. setattr(obj, 'x', 10)

- setattr на объекте-экземпляре создаёт (или изменяет) атрибут именно этого экземпляра. После этой строки obj.__dict__ содержит 'x': 10. Это не трогает атрибут класса C.x.

  1. delattr(C, 'x')

- Удаляет атрибут x из класса C. Теперь у класса C больше нет x. Но у экземпляра obj всё ещё есть собственный атрибут x = 10.

  1. getattr(obj, 'x', 99)

- При чтении obj.x сначала смотрят в словаре экземпляра — там найдено 10, поэтому возвращается 10. Значение по умолчанию 99 не используется.

Итог: программа выведет

text

Почему не другие варианты

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

Дополнительные примеры

1) Если удалить атрибут экземпляра — результат будет другим:

python

Вывод:

text

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

2) Если удалить и у экземпляра, и у класса:

python

Вывод:

text

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

3) Пример с исключением:

python

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

text

потому что атрибута нет ни у экземпляра, ни у класса, и мы не использовали getattr с запасным значением.

Выводы

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

Понимание этой последовательности важно для работы с наследованием, свойствами и динамическим изменением объектов в Python.

Report Page