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

时间:2012-11-14 13:29:40

标签: 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)

还有其他/更好的方法来处理此清理工作吗?我正在阅读'有',但它似乎只是在一个函数中有用。

5 个答案:

答案 0 :(得分:39)

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

那说,做

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__将创建并返回临时目录,而__exit__将删除它。

答案 1 :(得分:9)

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

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

import tempfile

with tempfile.TemporaryDirectory() as temp_dir:
    ... do stuff ...

对于早期的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 ...

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

答案 2 :(得分:2)

Bluewind所述,您必须确保将上下文管理器的yield部分包装在try:finally语句中,否则任何异常都不会在上下文管理器中真正正确处理。

来自Python 2.7 docs

  

在生成器产生的点处,执行嵌套在with语句中的块。然后在退出块之后恢复发生器。如果块中发生未处理的异常,则在生成器发生的点处将其重新加入。因此,您可以使用try ... except ... finally语句来捕获错误(如果有),或确保进行一些清理。如果仅为了记录异常或执行某些操作(而不是完全禁止它)而捕获异常,则生成器必须重新加载该异常。否则,生成器上下文管理器将向with语句指示已处理异常,并且将继续执行紧跟在with语句之后的语句。

此外,如果您使用的是Python 3.2+,那么您应该查看this little gem,其中包含了以上所有内容,并且很适合您

  

tempfile.TemporaryDirectory(suffix ='',prefix ='tmp',dir = None)

     

此函数使用mkdtemp()创建临时目录(提供的参数直接传递给基础函数)。生成的对象可以用作上下文管理器(请参阅使用语句上下文管理器)。完成上下文(或销毁临时目录对象)后,将从文件系统中删除新创建的临时目录及其所有内容。

     

可以从返回对象的name属性中检索目录名称。

     

可以通过调用cleanup()方法显式清理目录。

     

3.2版中的新功能。

答案 3 :(得分:2)

使用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()

答案 4 :(得分:1)

其他答案指出,您可以使用contextmanager或要求您的用户显式调用某种类型的清理功能。如果可以的话,这些都是很棒的事情。但是,有时没有地方进行此清理,因为您在大型应用程序内部,并且嵌套了多个层次,而上面没有人拥有清理方法或上下文管理器。

在这种情况下,您可以使用atexit:https://docs.python.org/2/library/atexit.html

import atexit

class MyClass:
  def __init__(self):
    self.tempfolder = tempfile.mkdtemp()
    atexit.register(shutil.rmtree, self.tempfolder)

  def ... #other stuff