正确的方法来清理Python类中的临时文件夹

Rob*_*ter 22 python destructor temporary-directory

我正在创建一个类,我想在其中生成一个文件夹的临时工作空间,该文件夹将在对象的生命周期中持续存在,然后被删除.我在def init中使用tempfile.mkdtemp()来创建空间,但我已经读过,我不能依赖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)
Run Code Online (Sandbox Code Playgroud)

有没有其他/更好的方法来处理这种清理?我正在阅读有关'with'但它似乎只对函数有用.

Kat*_*iel 40

警告:你永远不能保证临时文件夹会被删除,因为用户可能总是难以杀死你的进程,然后它就无法运行其他任何东西.

那说,做

temp_dir = tempfile.mkdtemp()
try:
    <some code>
finally:
    shutil.rmtree(temp_dir)
Run Code Online (Sandbox Code Playgroud)

由于这是一个非常常见的操作,Python有一种特殊的方法来封装"做某事,执行代码,清理":一个上下文管理器.您可以按如下方式编写自己的代码:

@contextlib.contextmanager
def make_temp_directory():
    temp_dir = tempfile.mkdtemp()
    try:
        yield temp_dir
    finally:
        shutil.rmtree(temp_dir)
Run Code Online (Sandbox Code Playgroud)

并用它作为

with make_temp_directory() as temp_dir:
    <some code>
Run Code Online (Sandbox Code Playgroud)

(请注意,这使用@contextlib.contextmanager快捷方式创建上下文管理器.如果要以原始方式实现一个,则需要使用__enter__和__exit__方法__enter__创建自定义类; 将创建并返回临时目录并__exit__删除它.

  • 上下文管理器对我来说很好,但我必须添加一个try:finally:在yield之后,否则如果使用temp dir的代码引发异常,它将无法清理.可能会在某处记录或者可能在python版本之间有所不同,但我想我会提到它. (3认同)
  • 请注意,Python 3.2添加了`tempfile.TemporaryDirectory()`,它与此答案显示的内容相同.对于3.2和更新版本,我会使用库模块代码; 对于较旧的Python版本,我会使用此代码,但我将该函数重命名为`TemporaryDirectory()`并记录它类似于3.2代码.https://docs.python.org/3/library/tempfile.html (3认同)
  • 上下文管理器可以在对象的生命周期内保留我的临时文件夹吗?看起来,一旦我留下“ with”语句,它将删除文件夹。我想我可以将文件夹作为参数传递到我的对象中,但是我希望将临时文件夹封装在类中。 (2认同)

Wrz*_*mft 12

我对此进行了一些尝试,并且我非常有信心,如果您不能使用上下文管理器,那么截至本文发布的最佳解决方案是:

\n
class MyClass(object):\n    def __init__(self):\n        self.tempfolder = tempfile.TemporaryDirectory()\n    \n    \xe2\x80\xa6\n\n    def __del__(self):\n        self.tempfolder.cleanup()\n
Run Code Online (Sandbox Code Playgroud)\n

(如果您不能确保被跟注,某些条件__del__可能是合理的。)__init__

\n

现在,除了使用较新的TemporaryDirectory而不是 之外mkdtemp,这与您所做的没有太大不同。为什么我仍然认为这是你能做的最好的事情?好吧,我测试了程序退出和类似的几种场景(全部在 Linux 上)并且:

\n
    \n
  • 我找不到临时文件夹未被删除的场景,尽管我希望 Python 可以决定MyClass不再需要相应的实例。自动删除最早会在 Python\xe2\x80\x99s 垃圾收集启发式允许的情况下发生。

    \n
  • \n
  • 您可以使用 \xe2\x80\x9chelp\xe2\x80\x9d 垃圾收集器del myinstance和gc.collect()。请注意,这del只会减少引用计数,因此这并不能确保垃圾收集可以发生并被__del__调用。

    \n
  • \n
  • 如果您确实想确保删除(临时目录),您可以显式调用myinstance.__del__(). 如果你能做到这一点,你也许也可以让MyClass自己成为一个上下文管理器。

    \n
  • \n
  • 临时文件夹持续存在的唯一情况是当我从操作系统 \xe2\x80\x93\xc2\xa0 硬杀 Python 时,在这种情况下,我看不到 Python 中的任何解决方案如何工作。

    \n
  • \n
  • atexit(如所建议的,例如,通过这个答案)不会改善这种情况:要么删除发生,无论如何atexit,或者它不会发生,即使有atexit。

    \n
  • \n
\n

  • 这是正确的答案。OP 规定他正在创建一个带有临时工作空间的“类”,该工作空间“将在对象的生命周期中持续存在”。这就是 `__del__` 的用途。 (2认同)

rco*_*oup 11

处理临时文件和目录的好方法是通过上下文管理器.这就是你如何使用tempfile.TemporaryFile或tempfile.NamedTemporaryFile - 一旦你退出with语句(通过正常的退出,返回,异常或其他任何东西)文件/目录,它的内容将从文件系统中删除.

对于Python 3.2+,它以tempfile.TemporaryDirectory为内置:

import tempfile

with tempfile.TemporaryDirectory() as temp_dir:
    ... do stuff ...
Run Code Online (Sandbox Code Playgroud)

对于早期的Python版本,您可以轻松地创建自己的上下文管理器来完成相同的操作.这里与@katrielalex答案的区别在于args 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 ...
Run Code Online (Sandbox Code Playgroud)

请注意,如果您的进程被硬杀死(例如kill -9),则目录将不会被清除.


Pet*_*ood 5

另一种替代方法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)
Run Code Online (Sandbox Code Playgroud)

然后使用上下文管理器:

from contextlib import closing

with closing(MyClass()) as my_object:
    my_object.do_stuff()
Run Code Online (Sandbox Code Playgroud)