【问题标题】:Is explicitly closing files important?显式关闭文件重要吗?
【发布时间】:2011-11-15 18:27:52
【问题描述】:

在 Python 中,如果您打开文件而不调用 close(),或者关闭文件但不使用 try-finally 或“with”语句,这是一个问题吗?或者作为一种编码实践,依靠 Python 垃圾收集来关闭所有文件就足够了吗?例如,如果这样做:

for line in open("filename"):
    # ... do stuff ...

...这是一个问题,因为文件永远无法关闭,并且可能发生阻止它被关闭的异常?还是肯定会因为文件超出范围而在 for 语句结束时关闭?

【问题讨论】:

  • 文件for块末尾超出范围。它的引用计数将归零,导致它自动关闭,但只有函数、类和模块在 Python 中定义范围,而不是其他复合语句。
  • 除非有问题,否则不是问题。在操作系统级别,脚本打开的任何文件都会在脚本退出时关闭,因此您不必担心在一次性工具脚本中关闭文件。但是,进程对它们可以维护的打开文件的数量有限制,因此长期或复杂的脚本可能需要更加小心。无论如何,关闭文件是一个好习惯。
  • @agf:文件没有超出范围是对的,但这与for 块和函数/类/模块之间的区别无关。它比这简单得多:对象没有范围,只有名称有。没有引用此对象的名称,因此这里没有任何东西可以留在范围内或超出范围。
  • @max 我的评论纠正了他的假设,即存在与for 循环相关的范围,并提到文件因完全不同的原因而关闭。它没有涉及 Python 中的 什么作用域,因为它在这里不相关。
  • @max 有一个隐式引用,范围是 for 循环......这是语义的一个参数

标签: python file garbage-collection


【解决方案1】:

在您的示例中,不能保证在解释器退出之前关闭文件。在当前版本的 CPython 中,该文件将在 for 循环结束时关闭,因为 CPython 使用引用计数作为其主要的垃圾收集机制,但这是一个实现细节,而不是该语言的特性。 Python 的其他实现不能保证以这种方式工作。例如 IronPython、PyPy 和 Jython 不使用引用计数,因此不会在循环结束时关闭文件。

依赖 CPython 的垃圾回收实现是一种不好的做法,因为它会降低您的代码的可移植性。如果您使用 CPython,可能不会发生资源泄漏,但如果您切换到不使用引用计数的 Python 实现,则需要检查所有代码并确保所有文件都正确关闭。

您的示例使用:

with open("filename") as f:
     for line in f:
        # ... do stuff ...

【讨论】:

  • 使用with open() as f完成后会自动关闭文件吗?
  • @Rohan 是的,这就是 with 语句提供的小魔法,但当然,要使这个魔法起作用,对象必须具有特殊的方法 __enter____exit__,在后者对象执行close 和任何其他需要在with 语句末尾完成的清理工作...
  • 仅供参考:此答案仅解释“何时关闭”,但没有解释“如果它保持打开状态会怎样”。对于后者,请阅读“如果文件保持打开会怎样?”参与这个答案(askubuntu.com/questions/701491/…
  • 此外,不关闭文件可能会导致文件被截断,因为文件内容尚未刷新。
  • 所以如果我不关闭文件,一旦程序停止运行,我是否可以确定恢复内存?还是我真的必须退出整个解释器?
【解决方案2】:

当文件不再被引用时,一些 Python 会自动关闭文件,而另一些则不会,当 Python 解释器退出时,由操作系统来关闭文件。

即使对于将为您关闭文件的 Python,时间也不能保证:它可能是立即的,也可能是几秒/分钟/小时/天之后。

因此,虽然您使用的 Python 可能不会遇到问题,但让文件保持打开状态绝对不是一个好习惯。事实上,在 cpython 3 中,您现在会收到警告,如果您不这样做,系统必须为您关闭文件。

道德:清理自己。 :)

【讨论】:

  • 文件在 CPython 中不再被引用时会被关闭,但这不是语言特性。如果是的话,你可以很高兴地依赖它。
【解决方案3】:

虽然在这种特殊情况下使用这种结构是相当安全的,但对于推广这种做法有一些注意事项:

  • run 可能会用完文件描述符,虽然不太可能,但想象一下寻找这样的错误
  • 您可能无法在某些系统上删除所述文件,例如win32
  • 如果你运行 CPython 以外的任何东西,你不知道文件何时为你关闭
  • 如果以写入或读写模式打开文件,则不知道何时刷新数据

【讨论】:

    【解决方案4】:

    该文件确实被垃圾收集,因此被关闭。 GC 决定何时关闭,而不是您。显然,这不是推荐的做法,因为如果您在使用完文件后不立即关闭文件,则可能会达到打开文件句柄的限制。如果在您的 for 循环中,您打开更多文件并让它们逗留怎么办?

    【讨论】:

    • 但是,如果您在该 for 循环中打开了其他文件,则无论您是否明确关闭其中任何一个文件,都会同时打开多个文件。您是说文件一旦超出范围就不一定会被垃圾收集,因此如果明确完成它会更快地关闭?什么时候发生异常(当你使用 with/try-finally 与不这样做时)?
    • 在 CPython 中,引用计数将导致它在 for 语句之后被收集——您不必等待下一次垃圾收集运行。
    【解决方案5】:

    您好,当您要在同一个 python 脚本中使用文件描述符的内容时,关闭文件描述符非常重要。经过这么长时间的调试,我今天自己意识到了。原因是只有在您关闭文件描述符并且更改会影响文件后,内容才会被编辑/删除/保存!

    所以假设您有这样的情况,您将内容写入一个新文件,然后在没有关闭 fd 的情况下,您在另一个读取其内容的 shell 命令中使用该文件(而不是 fd)。在这种情况下,您将无法按预期获得 shell 命令的内容,并且如果您尝试调试,您​​将无法轻松找到错误。你也可以在我的博文http://magnificentzps.blogspot.in/2014/04/importance-of-closing-file-descriptor.html阅读更多内容

    【讨论】:

    • 为什么不直接使用 file.seek(0) 搜索到文件的开头?关闭然后重新打开同一个文件可能会更昂贵。
    【解决方案6】:

    在 I/O 过程中,数据被缓冲:这意味着它在被写入文件之前被保存在一个临时位置。

    Python 不会刷新缓冲区——也就是说,将数据写入文件——直到它确定你已经完成了写入。一种方法是关闭文件。

    如果你在没有关闭的情况下写入文件,数据将不会进入目标文件。

    【讨论】:

    • 我认为这在 Linux 中根本不是真的。文件在 open("file", "w") 后立即创建,在 .write("whatever") 后立即写入。
    【解决方案7】:

    Python 使用 close() 方法关闭打开的文件。文件关闭后,您将无法再次读取/写入该文件中的数据。

    如果您再次尝试访问同一个文件,则会引发 ValueError,因为该文件已经关闭。

    如果引用对象已分配给另一个文件,Python 会自动关闭该文件。关闭文件是一种标准做法,因为它可以降低被无端修改的风险。

    解决此问题的另一种方法是.... with statement

    如果您使用 with 语句打开文件,则会保留一个临时变量以用于访问该文件,并且只能使用缩进块访问它。 With 语句本身在执行缩进代码后调用 close() 方法。

    语法:

    with open('file_name.text') as file:
    
        #some code here
    

    【讨论】:

      猜你喜欢
      • 2019-08-01
      • 1970-01-01
      • 2018-05-17
      • 1970-01-01
      • 2010-10-27
      相关资源
      最近更新 更多