delattr(): как удалить атрибут объекта в Python
Функция delattr() в Python удаляет атрибут у объекта, если сам объект это разрешает, а имя атрибута передается строкой. Разберем синтаксис, удаление атрибутов экземпляра и класса, разницу между delattr() и оператором del, типичные ошибки и способ запретить удаление через метод __delattr__.

Содержание статьи
Синтаксис delattr()
Функция встроенная, импортировать ее не нужно. Она принимает два обязательных аргумента и ничего не возвращает (результат вызова равен None).
delattr(object, name)
object- объект, у которого удаляется атрибут: экземпляр, класс, модуль;name- имя атрибута строкой, например'color'.
Вызов delattr(obj, 'color') полностью равносилен записи del obj.color. Функция входит в четверку инструментов для работы с атрибутами по имени вместе с getattr(), setattr() и hasattr(). Остальные встроенные функции собраны на странице встроенных методов и функций.
Удаление атрибута экземпляра
Атрибуты, созданные в __init__ через self, хранятся в словаре __dict__ конкретного объекта. delattr() убирает запись из этого словаря, другие экземпляры класса не затрагиваются.
class User:
def __init__(self, name, token):
self.name = name
self.token = token
u1 = User('anna', 'abc123')
u2 = User('boris', 'xyz789')
delattr(u1, 'token')
print(u1.__dict__) # {'name': 'anna'}
print(hasattr(u1, 'token')) # False
print(u2.token) # xyz789
Если у класса есть атрибут с тем же именем, после удаления атрибута экземпляра обращение вернет значение класса. Это удобно для сброса настройки к значению по умолчанию.
class Button:
color = 'gray' # значение по умолчанию
b = Button()
b.color = 'red'
print(b.color) # red
delattr(b, 'color')
print(b.color) # gray
Удаление атрибута класса
Если передать в delattr() сам класс, атрибут исчезнет у класса и, соответственно, у всех его экземпляров, которые не имеют собственного атрибута с таким именем.
class Coordinate:
x = 10
y = -5
z = 0
point = Coordinate()
print(point.x, point.y, point.z) # 10 -5 0
delattr(Coordinate, 'z')
print(point.x, point.y) # 10 -5
print(point.z) # AttributeError
Обратное не работает: удалить атрибут класса через экземпляр нельзя. Вызов delattr(point, 'x') вызовет AttributeError, потому что в point.__dict__ такого ключа нет. Подробнее о том, где хранятся атрибуты, рассказано в статье о классах и объектах.
delattr() или del
Обе записи выполняют одно и то же и вызывают метод __delattr__ объекта. Разница только в том, как задается имя.
| Критерий | del obj.attr | delattr(obj, name) |
|---|---|---|
| Имя атрибута | Пишется в коде | Строка, может быть в переменной |
| Читаемость | Выше | Ниже |
| Удаление по списку имен | Невозможно без eval | Естественно в цикле |
| Скорость | Чуть быстрее | Есть накладные расходы на вызов функции |
Правило выбора: если имя атрибута известно при написании кода, используйте del. delattr() нужен, когда имя приходит из данных: из конфигурации, списка полей, ответа API.
class Profile:
def __init__(self):
self.login = 'anna'
self.password = 'secret'
self.phone = '+7 900 000-00-00'
private_fields = ['password', 'phone']
p = Profile()
for field in private_fields:
if hasattr(p, field):
delattr(p, field)
print(vars(p)) # {'login': 'anna'}
Ошибки и ограничения
delattr() выбрасывает исключение, если удалить атрибут нельзя. Основные случаи:
- Атрибута нет или он принадлежит классу, а не экземпляру:
AttributeError. - Атрибут встроенного типа:
delattr(5, 'real')или удаление метода у строки дают AttributeError, встроенные объекты не позволяют менять свои атрибуты. - Свойство (property) без метода deleter: AttributeError с сообщением о том, что атрибут нельзя удалить.
- Поле замороженного датакласса:
dataclasses.FrozenInstanceError. О датаклассах есть отдельная статья Dataclasses в Python.
Чтобы удаление отсутствующего атрибута не роняло программу, проверьте его заранее через hasattr() или перехватите исключение:
try:
delattr(obj, 'cache')
except AttributeError:
pass # атрибута не было, ничего делать не нужно
Объекты классов с __slots__ атрибуты удалять позволяют: слот остается, но становится пустым, и чтение после удаления дает AttributeError до следующего присваивания.
Метод __delattr__ и защита атрибутов
При любом удалении атрибута, через del или через delattr(), Python вызывает метод __delattr__(self, name). Переопределив его, можно запретить удаление важных полей, записать событие в журнал или очистить связанные данные. Внутри метода фактическое удаление выполняют вызовом super().__delattr__(name), иначе атрибут останется на месте.
class Config:
protected = {'db_url'}
def __init__(self):
self.db_url = 'sqlite:///app.db'
self.debug = True
def __delattr__(self, name):
if name in self.protected:
raise AttributeError(f'Нельзя удалить {name}')
print('Удаляю', name)
super().__delattr__(name)
cfg = Config()
delattr(cfg, 'debug') # Удаляю debug
del cfg.db_url # AttributeError: Нельзя удалить db_url
Не пишите внутри __delattr__ конструкцию del self.name: она снова вызовет __delattr__ и приведет к бесконечной рекурсии. Описание функции есть в официальной документации.
Где delattr() применяется на практике
В прикладном коде функция встречается нечасто, но в нескольких ситуациях без нее не обойтись:
- Сброс кеша. Значение, вычисленное через
functools.cached_property, хранится в атрибуте экземпляра, и его удаление заставит пересчитать значение при следующем обращении. - Тесты. После подмены атрибута модуля или класса в тесте его удаляют, чтобы вернуть исходное поведение.
- Сериализация. Перед сохранением объекта удаляют служебные поля, например соединение с базой, которое нельзя упаковать в pickle.
- Обработка динамических данных. Поля, пришедшие из внешнего источника, удаляют по списку имен.
from functools import cached_property
class Report:
@cached_property
def total(self):
print('считаю...')
return 42
r = Report()
r.total # считаю...
r.total # берется из кеша
delattr(r, 'total')
r.total # считаю... (пересчет)
Частые вопросы
Ответы на вопросы о функции delattr().
Что возвращает delattr()?
Ничего, результат равен None. Об успехе говорит отсутствие исключения, проверить результат можно через hasattr().
Можно ли удалить метод класса через delattr()?
Да, метод является атрибутом класса: delattr(MyClass, 'method_name'). После этого вызов метода у любых экземпляров даст AttributeError, если метод не унаследован от родителя.
Можно ли удалить переменную модуля?
Можно, модуль тоже объект: delattr(module, 'name'). Для глобальной переменной текущего модуля проще написать del name.
Чем delattr() отличается от присваивания None?
При присваивании None атрибут остается и hasattr() возвращает True. После delattr() атрибута нет совсем, а обращение к нему вызывает AttributeError или возвращает значение из класса.