文件对象在引用计数为零时会自动关闭吗?

41 投票
4 回答
24144 浏览
提问于 2025-04-15 16:36

我原以为文件对象在引用计数变为0的时候会立刻关闭,所以这行代码:

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

会读取文件的内容并立即关闭文件。然而,在阅读了关于在使用Python文件对象的迭代器时,close()是否必要的回答后,我觉得事情并不是这样,调用.close()来关闭文件对象是总是必要的。

那么,上面的那行代码真的像我想的那样工作吗?即使它真的这样做了,这样做在Python中算不算是个好习惯呢?

4 个回答

12

关于Python的CPython实现:是的,当它的引用计数降到零时,系统会确保它被关闭。

关于Python作为一种抽象语言(比如Jython、IronPython等):不,这并不能保证会被关闭。特别是,有些Python的实现可能选择不使用引用计数,而是采用其他形式的垃圾回收(GC)。

参考资料:

27

如果你想确保代码能正常运行,我会这样写:

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 
35

答案就在你提供的链接里。

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

  • 你其实无法控制这个过程发生的具体时间。

    比如,CPython(Python的一种实现)使用引用计数来确定何时释放资源,这样你可以预测对象何时会被销毁。但其他版本就不一定了。例如,Jython和IronPython使用的是JVM和.NET的垃圾回收器,这些垃圾回收器只有在需要释放内存时才会释放(和结束)对象,有些对象可能会一直保留到程序结束。

    而且即使是CPython,它的垃圾回收算法将来也可能会改变,因为引用计数并不是特别高效。

  • 如果在关闭文件时出现异常,而这个异常是在文件对象被销毁的时候发生的,你也无能为力,因为你根本不知道发生了什么。

撰写回答