我经常看到这种代码:
filecontent = open(thefilename).read()
我想知道open
在这种情况下创建的文件对象是什么:它是隐式关闭的,还是在某处保持打开状态?
我经常看到这种代码:
filecontent = open(thefilename).read()
我想知道open
在这种情况下创建的文件对象是什么:它是隐式关闭的,还是在某处保持打开状态?
我想知道在这种情况下由 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 对它们进行排序”进行破解。