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