文件对象在引用计数为零时会自动关闭吗?
我原以为文件对象在引用计数变为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,它的垃圾回收算法将来也可能会改变,因为引用计数并不是特别高效。
如果在关闭文件时出现异常,而这个异常是在文件对象被销毁的时候发生的,你也无能为力,因为你根本不知道发生了什么。