Сообщить об ошибке.

Объект finalize против метода __del__

Предположим, надо создать класс, экземпляры которого представляют временные каталоги. Каталоги должны быть удалены с их содержимым, когда происходит первое из следующих событий:

  • объект собирается сборщиком мусора,
  • вызывается метод remove() объекта,
  • программа заканчивает работу.

Можно попытаться реализовать класс с помощью метода __del__() следующим образом:

class TempDir:
    def __init__(self):
        self.name = tempfile.mkdtemp()

    def remove(self):
        if self.name is not None:
            shutil.rmtree(self.name)
            self.name = None

    @property
    def removed(self):
        return self.name is None

    def __del__(self):
        self.remove()

Методы __del__() не препятствуют сборке мусора в ссылочных циклах, а глобальные переменные модуля не принудительно отключаются во время выключения интерпретатора. Так что этот код должен работать без проблем на CPython.

Однако обработка методов __del__() общеизвестно зависит от реализации, которая в свою очередь зависит от внутренних деталей реализации сборщика мусора интерпретатора.

Более надежной альтернативой может быть определение weakref.finalize(), который ссылается только на конкретные нужные ему функции и объекты, а не на доступ к полному состоянию объекта:

class TempDir:
    def __init__(self):
        self.name = tempfile.mkdtemp()
        self._finalizer = weakref.finalize(self, shutil.rmtree, self.name)

    def remove(self):
        self._finalizer()

    @property
    def removed(self):
        return not self._finalizer.alive

Определяемый так finalize получает только ссылку на детали, необходимые для надлежащей очистки каталога. Если объект никогда не будет собран сборщиком мусора, то finalizer все равно будет вызываться при выходе.

Другое преимущество weakref.finalize() на основе слабых ссылок заключается в том, что они могут использоваться для регистрации finalize для классов, где определение контролируется третьей стороной, например при запуске кода при выгрузке модуля:

import weakref, sys

def unloading_module():
    # Неявная ссылка на 'globals' модуля из тела функции
    weakref.finalize(sys.modules[__name__], unloading_module)

Примечание. Если создать объект finalize в "демоническом" потоке сразу после выхода из программы, то существует вероятность, что finalize при выходе вызван не будет.