【问题标题】:Right way to clean up a temporary folder in Python class在 Python 类中清理临时文件夹的正确方法
【发布时间】:2012-11-03 00:04:52
【问题描述】:

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

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

【问题讨论】:

  • 我认为唯一可靠的解决方案总是涉及明确地进行清理。我认为您在问题中设想的可靠的自动清理解决方案是不可能的。
  • @PedroRomano false:Python 的上下文管理器正是为此目的。
  • @katrielalex:甚至需要明确输入上下文管理器。
  • @PedroRomano 是的——但你总是需要例如打开一个文件。上下文管理器的重点在于它处理清理。

标签: python destructor temporary-directory


【解决方案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__将创建并返回临时目录,__exit__ 将其删除。

【讨论】:

  • 上下文管理器能否在对象的生命周期内保留我的临时文件夹?一旦我离开“with”语句,它似乎会删除该文件夹。我想我可以将文件夹作为参数传递到我的对象中,但我希望将临时文件夹封装在类中。
  • with 语句的定义是当你离开它时它会关闭对象。如果你想让你的临时目录在其他地方关闭,你需要在那里明确地关闭它并自己处理异常。
  • 上下文管理器对我来说工作得很好,但是我必须在 yield 周围添加一个 try: finally: 否则如果使用 temp dir 的代码引发异常,它就不会清理。可能在某处记录或可能在 python 版本之间有所不同,但我想我会提到它。
  • 该行为是documented:“如果块中发生未处理的异常,它会在生成器中在产生的点重新引发。因此,您可以使用try...except...finally 语句来捕获错误(如果有),或确保进行一些清理。”
  • 请注意,Python 3.2 添加了tempfile.TemporaryDirectory(),它的作用与此答案显示的相同。对于 3.2 和更新版本,我将使用库模块代码;对于较旧的 Python 版本,我会使用此代码,但我会将函数重命名为 TemporaryDirectory() 并记录它类似于 3.2 代码。 docs.python.org/3/library/tempfile.html
【解决方案2】:

处理临时文件和目录的好方法是通过上下文管理器。这就是您可以使用tempfile.TemporaryFile 或tempfile.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),则不会清理目录。

【讨论】:

  • 我喜欢这个!回答!
【解决方案3】:

使用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】:

    其他答案指出您可以使用上下文管理器或要求您的用户显式调用某种类型的清理功能。如果可以的话,这些都很棒。但是,有时由于您在一个大型应用程序中并且向下嵌套了多个层,并且您上方没有清理方法或上下文管理器,因此有时无处可进行此清理。

    在这种情况下,您可以使用 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
    

    【讨论】:

      【解决方案5】:

      正如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 版中的新功能。

      【讨论】:

      • Python 3 cleanup() in with: 请注意cleanup() 方法是在上下文管理器with 中无用的东西,因为这是tempfile.TemporaryDirectory() 的方法 - if 在上下文管理器中使用,返回一个 string ! (不是对象本身)。除此之外,tempfile.TemporaryDirectory.cleanup() 不仅“清理”其内容,它还删除了目录本身!
      猜你喜欢
      • 1970-01-01
      • 2021-12-27
      • 1970-01-01
      • 2015-05-14
      • 1970-01-01
      • 2016-01-04
      • 1970-01-01
      • 2017-10-29
      • 1970-01-01
      相关资源
      最近更新 更多