34

我正在创建一个类,我想在其中生成一个临时文件夹工作区,这些文件夹将在对象的生命周期内持续存在,然后被删除。我在 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)

是否有另一种/更好的方法来处理这种清理?我正在阅读有关“与”的信息,但它似乎只在函数中有用。

4

5 回答 5

51

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

也就是说,做

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__删除它。

于 2012-11-14T13:42:59.657 回答
24

处理临时文件和目录的一个好方法是通过上下文管理器。这就是您可以使用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),那么目录将不会被清理。

于 2014-02-21T00:07:04.060 回答
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()
于 2014-10-24T07:13:05.363 回答
4

其他答案指出,您可以使用上下文管理器或要求您的用户显式调用某种类型的清理功能。如果可以的话,这些都很棒。然而,有时因为你在一个大型应用程序中并且你嵌套了多个层,所以没有地方可以连接这个清理,而且你上面没有清理方法或上下文管理器。

在这种情况下,您可以使用 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
于 2019-02-21T22:36:41.930 回答
2

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

来自Python 2.7 文档

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

此外,如果您使用的是 Python 3.2+,您应该查看这个小宝石,它已经为您很好地包装了以上所有内容

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

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

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

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

3.2 版中的新功能。

于 2013-10-29T11:30:11.027 回答