【问题标题】:Does a File Object Automatically Close when its Reference Count Hits Zero?当引用计数为零时文件对象是否会自动关闭?
【发布时间】:2010-12-22 12:24:03
【问题描述】:

我的印象是文件对象在引用计数达到 0 时立即关闭,因此行:

foo = open('foo').read()

会得到文件的内容并立即关闭文件。但是,在阅读了Is close() necessary when using iterator on a Python file object 的答案后,我的印象是这不会发生,并且在文件对象上调用.close()总是必要的。

上面这行代码是否符合我的想法?即使是这样,它是 Python 式的吗?

【问题讨论】:

  • 你问的是Python文件对象还是操作系统级别的底层文件对象? OS 层与 Python 层是分开的。你问的是哪个?
  • 我很乐意解释这两个级别发生的情况。更具体地说,我想知道编写该行代码是否会导致我的应用程序出现问题。我假设当 Python 关闭文件对象时,底层对象也会被释放。

标签: python


【解决方案1】:

答案在您提供的链接中。

垃圾收集器在销毁文件对象时会关闭文件,但是:

  • 你无法真正控制它何时发生。

    虽然 CPython 使用引用计数来确定性地释放资源 (因此您可以预测对象何时会被销毁)其他版本不必这样做。 例如,Jython 或 IronPython 都使用 JVM 和 .NET 垃圾收集器,它们 仅在需要恢复内存时释放(并完成)对象 并且在程序结束之前可能不会对某些对象执行此操作。 甚至对于 CPython GC 算法将来也可能会随着引用计数而改变 效率不高。

  • 如果在文件对象销毁时关闭文件时抛出异常, 你真的无能为力,因为你不知道。

【讨论】:

  • 那么,我可以从一般意义上推断,依赖于我正在使用的 Python 版本中如何实现垃圾收集的细节是一个坏主意,而不是 Pythonic,因此这是为什么我在问题中的代码行不正确?
  • @Brent:不一定是不正确的(如果您决定取决于您的实施的具体行为)。我不知道 Pythonic ;-)。但是在任何依赖垃圾收集器(调整为释放内存)来清理其他资源的托管环境中都是错误的方向。当然,如果它只是简短的脚本 并且 您只需读取文件(就像在您的示例中一样),让 GC 甚至操作系统关闭文件是可以的。
【解决方案2】:

如果你想确定,我会这样写代码:

from __future__ import with_statement

with open('foo') as f:
    foo = f.read()

这样,即使出现异常,您的文件也会按预期关闭。


很久以后:这里有一些带有import dis 的代码,以显示编译器如何区别对待它们。

>>> def foo(filename):
...     with open(filename) as f:
...         return f.read()
... 
>>> def bar(filename):
...     return open(filename).read()
... 
>>> from dis import dis
>>> 
>>> dis(foo)
  2           0 LOAD_GLOBAL              0 (open)
              3 LOAD_FAST                0 (filename)
              6 CALL_FUNCTION            1
              9 DUP_TOP             
             10 LOAD_ATTR                1 (__exit__)
             13 ROT_TWO             
             14 LOAD_ATTR                2 (__enter__)
             17 CALL_FUNCTION            0
             20 STORE_FAST               1 (_[1])
             23 SETUP_FINALLY           23 (to 49)
             26 LOAD_FAST                1 (_[1])
             29 DELETE_FAST              1 (_[1])
             32 STORE_FAST               2 (f)

  3          35 LOAD_FAST                2 (f)
             38 LOAD_ATTR                3 (read)
             41 CALL_FUNCTION            0
             44 RETURN_VALUE        
             45 POP_BLOCK           
             46 LOAD_CONST               0 (None)
        >>   49 WITH_CLEANUP        
             50 END_FINALLY         
             51 LOAD_CONST               0 (None)
             54 RETURN_VALUE        
>>> dis(bar)
  2           0 LOAD_GLOBAL              0 (open)
              3 LOAD_FAST                0 (filename)
              6 CALL_FUNCTION            1
              9 LOAD_ATTR                1 (read)
             12 CALL_FUNCTION            0
             15 RETURN_VALUE 

【讨论】:

  • 我明白这一点,但我特意询问代码是否按照我的想法执行。如果不是,它是做什么的,如果是,它是正确的方式吗?
  • 正如@Edward Loper 和@tomekszpakowicz 所说,会发生什么将取决于实现,因此“代码是否按照我的想法执行”将取决于它在哪里运行。我提供的解决方案独立于实现。
【解决方案3】:

对于python的cpython实现:是的,当它的引用计数变为零时,保证关闭。

对于作为抽象语言的python(例如,包括Jython、IronPython等):不,不保证是封闭的。特别是,Python 的实现可能会选择不使用引用计数,而是使用其他形式的 GC。

参考资料:

【讨论】:

    【解决方案4】:

    不,Python 优化了删除未使用的对象,因此它可能永远不会关闭您的文件(确定在您的脚本结束时退出它会清理)。 @hughdbrown 指出了很好的解决方案。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2011-01-25
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多