(часто спрашивают на собеседованиях)
Этот магический метод позволяет определить поведение экземпляра пользовательского типа при готовности интерпретатора уничтожить его.
Многие думают, что del x вызывает метод x.__del__. Это неправда. Python использует механизм подсчета ссылок, и del x – лишь один из способов уменьшить количество ссылок на 1.
📎 Примеры. Определим такой класс, где будем следить за вызовом его метода __del__:
class Bazooka:
def __del__(self):
print('Bazooka.__del__()')
>>> b = Bazooka() # тут мы привяжем новый объект Bazooka() к имени переменной b (1 ссылка)
>>> del b # тут мы удаляем имя b, объект отвязывется, ссылок - 0, вызывается деструктор.
Bazooka.__del__()
>>> b = Bazooka()
>>> b = None # имя b не удалено, но объект будет отвязан от него и деструктор вызовется
Bazooka.__del__()
>>> b = Bazooka()
>>> b2 = b # объект привязан и к b, и к b2 (ссылок - 2)
>>> del b # удаляем первый, ссылок - 1, деструктор не вызван
>>> del b2 # удаляем второй, ссылок - 0, деструктор вызывается
Bazooka.__del__()
Важные нюансы:
❗️ Не гарантируется, что метод будет вызван для объектов, всё ещё существующих на момент выхода интерпретатора.
❗️#Если метод определён в базовом классе, то потомок должен вызывать его явно, чтобы удаление части экземпляра, реализуемой базовым классом произошло без ошибок.
❗️ Ссылка на объект может остаться при броске исключения в sys.exc_traceback (исправляется путем sys.last_traceback = None)
❗️ До Python 3.4 сборщик мусора не мог разорвать циклические ссылки с объектами, у которых определен __del__.
❗️ Не стоит путать __del__ и __delete__, __delete__ используется напрямую крайне редко, и нужен он для объектов-дескрипторов.