4

我经常看到这种代码:

filecontent = open(thefilename).read()

我想知道open在这种情况下创建的文件对象是什么:它是隐式关闭的,还是在某处保持打开状态?

4

1 回答 1

12

我想知道在这种情况下由 open 创建的文件对象是什么:它是隐式关闭的,还是在某个地方保持打开状态?

它保持打开状态,直到垃圾收集器发现没有人可以再访问它并销毁它,此时它被析构函数关闭。

Python 语言不保证何时会发生这种情况(当然,除非是在您无法再访问它之后)。

但是,CPython 实现(您可能正在使用,因为它是截至 2013 年 5 月唯一有效的 3.x 实现)使用引用计数(加上循环检测器)进行垃圾收集,这意味着只要最后一个引用消失(除非它在某个时候参与了循环),对象被销毁。

因此,在 CPython 中,在大多数情况下,文件将在您从函数返回、为filecontent或分配新值时立即关闭del filecontent。许多快速而肮脏的代码都依赖于此。

但是 Jython 和 IronPython 依赖于 Java/.NET 垃圾收集器,它以复杂而奇特的方式定期检查垃圾,而不是动态跟踪,因此无法保证何时收集到任何东西。PyPy 有多个选项,具体取决于它的配置方式。

而且,即使在 CPython 中,垃圾也可能在没有可见引用后仍然存在,因为例如,您在调试器中运行它并最终得到一些不可见的引用。或者,如果文件涉及引用循环,它可能永远不会关闭。

所以,除了快速&脏代码之外,你不应该依赖这种行为。基本上,如果文件在程序完成之前保持打开状态是可以接受的,那很好。否则,不要这样做。

正确的方法是使用以下with语句:

with open(thefilename) as f:
    filecontent = f.read()

这保证了f.close()一旦with语句完成就会被调用。

每隔一段时间,人们就会建议一种将其变成单线的方法,Guido 总是回答类似“with open(thefilename) as f: filecontent = f.read()已经是单线。这有点糟糕,但远没有你建议的那么糟糕。”

但实际上,还有一个更好的答案:编写一个包装它的函数:

def read_whole_file(filename):
    with open(thefilename) as f:
        return f.read()

接着:

filecontent = read_whole_file(thefilename)

干净、简洁、易读……没有任何借口可以“open让 GC 对它们进行排序”进行破解。

于 2013-05-06T19:38:14.537 回答