Правильный способ очистки временной папки в классе Python
Я создаю класс, в котором я хочу создать временное рабочее пространство папок, которое будет сохраняться для жизни объекта, а затем будет удалено. Я использую tempfile.mkdtemp() в def init, чтобы создать пробел, но я прочитал, что я не могу полагаться на вызываемый del.
Я хочу что-то вроде этого:
class MyClass:
def __init__(self):
self.tempfolder = tempfile.mkdtemp()
def ... #other stuff
def __del__(self):
if os.path.exists(self.tempfolder): shutil.rmtree(self.tempfolder)
Есть ли другой/лучший способ справиться с этой очисткой? Я читал о "с", но, похоже, он полезен только в функции.
Ответы
Ответ 1
Предостережение: вы никогда не сможете гарантировать, что временная папка будет удалена, потому что пользователь всегда может убить ваш процесс, а затем ничего не может запустить.
Тем не менее,
temp_dir = tempfile.mkdtemp()
try:
<some code>
finally:
shutil.rmtree(temp_dir)
Поскольку это очень распространенная операция, Python имеет особый способ инкапсулировать "сделать что-нибудь, выполнить код, очистить": менеджер контекста. Вы можете написать свой собственный:
@contextlib.contextmanager
def make_temp_directory():
temp_dir = tempfile.mkdtemp()
try:
yield temp_dir
finally:
shutil.rmtree(temp_dir)
и использовать его как
with make_temp_directory() as temp_dir:
<some code>
(Обратите внимание, что для создания диспетчера контекстов используется ярлык @contextlib.contextmanager
. Если вы хотите реализовать его по-своему, вам нужно создать собственный класс с методами __enter__
и __exit__
, __enter__
создаст и вернет каталог temp и __exit__
удалит его.
Ответ 2
Хороший способ справиться с временными файлами и каталогами - через менеджер контекста. Вот как вы можете использовать tempfile.TemporaryFile или tempfile.NamedTemporaryFile - после того, как вы вышли из инструкции with
(через обычный выход, возврат, исключение или что-то еще), файл/каталог и его содержимое будут удалены из файловой системы.
Для Python 3.2+ он встроен в tempfile.TemporaryDirectory:
import tempfile
with tempfile.TemporaryDirectory() as temp_dir:
... do stuff ...
Для более ранних версий Python вы можете легко создать свой собственный менеджер контекстов, чтобы сделать то же самое. Отличие от ответа @katrielalex заключается в передаче аргументов mkdtemp()
и блока try/finally, чтобы убедиться, что каталог очищается, если возникает исключение.
import contextlib
import shutil
@contextlib.contextmanager
def temporary_directory(*args, **kwargs):
d = tempfile.mkdtemp(*args, **kwargs)
try:
yield d
finally:
shutil.rmtree(d)
# use it
with temporary_directory() as temp_dir:
... do stuff ...
Обратите внимание, что если ваш процесс сильно убит (например, kill -9
), то каталоги не будут очищены.
Ответ 3
Как указано Bluewind, вы должны обязательно обернуть часть доходности контекстного менеджера внутри инструкции try: finally, иначе любые исключения на самом деле не будут корректно обрабатываться внутри диспетчера контекстов.
От Python 2.7 docs
В момент, когда генерирует генератор, выполняется блок, вложенный в оператор с. Затем генератор возобновляется после выхода блока. Если в блоке возникает необработанное исключение, оно ререйзируется внутри генератора в точке, где произошел выход. Таким образом, вы можете использовать команду try... except... finally, чтобы уловить ошибку (если она есть) или убедиться, что происходит некоторая очистка. Если исключение попадает в ловушку только для того, чтобы зарегистрировать его или выполнить какое-либо действие (а не полностью его подавить), генератор должен сделать репозицию этого исключения. В противном случае диспетчер контекста генератора укажет в инструкции с, что обработано исключение, и выполнение возобновится с оператором сразу после оператора с.
Также, если вы используете Python 3.2+, вы должны проверить этот маленький драгоценный камень, который имеет все вышеперечисленное, /p >
tempfile.TemporaryDirectory(suffix = '', prefix = 'tmp', dir = None)
Эта функция создает временный каталог с использованием mkdtemp() (предоставленные аргументы передаются непосредственно базовой функции). Полученный объект можно использовать в качестве менеджера контекста (см. Раздел "Контекстные менеджеры выражений" ). По завершении контекста (или уничтожения временного объекта каталога) вновь созданный временный каталог и все его содержимое удаляются из файловой системы.
Имя каталога можно получить из атрибута name возвращаемого объекта.
Каталог можно явно очистить, вызвав метод cleanup().
Новое в версии 3.2.
Ответ 4
Другая альтернатива, использующая contextlib
, - сделать ваш объект закрываемым и использовать контекстный менеджер closing
.
class MyClass:
def __init__(self):
self.tempfolder = tempfile.mkdtemp()
def do_stuff():
pass
def close(self):
if os.path.exists(self.tempfolder):
shutil.rmtree(self.tempfolder)
Затем с менеджером контекста:
from contextlib import closing
with closing(MyClass()) as my_object:
my_object.do_stuff()