Python中已打开的文件描述符

4
当我在IPython3 shell中使用这段代码时。
 >>>data = open('file').read()

然后检查打开的文件描述符:

 lsof | grep file

我发现了一个空列表

当我使用以下代码时:

>>>open('file')

lsof显示了两个条目。问题是为什么第一个操作关闭fd而第二个操作没有?我认为垃圾回收器必须删除没有引用的文件对象。

我知道解释器中的'_'变量,当我重新分配值时。

>>>111
>>>_
111

但是描述符仍然保持打开状态。当我重复时


>>>open('file')

每次有 n 个打开的描述符,总共有 2 * n 个打开的描述符。

你使用的是哪个Python shell?在默认的Python 2.7.3 shell中,文件描述符会在第二个表达式(重新分配“_”)输入后立即释放。 - user4815162342
没问题;但是,我无法在 Python 3.3.0 中重复它。我执行 open('somefile'),在另一个 shell 中,lsof 显示该文件已打开。然后我执行 1+1,在 shell 中 lsof 显示该文件未打开。 - user4815162342
3个回答

4
在第二个例子中,文件句柄被交互式解释器变量 _ 保留,这使得您可以访问最后评估的表达式。如果您评估另一个表达式,例如 1+1,则会注意到该文件不再被 lsof 报告为打开状态。
正如 Mike Byers 指出的那样,此行为特定于 CPython,并且甚至具体取决于如何使用文件对象。为了确保不管代码如何执行都关闭文件,请使用 with 语句:
with open('file') as fp:
    data = fp.read()

如果我重复n次使用open('file'),就会打开2 * n个描述符。 - adray
@adray,你是如何得出这个结论的?调用一次open应该对应一个打开的文件描述符。 - user4815162342
@adray 我无法在Python 2.7.3中重复这个问题,请参见我在问题下面的评论。 - user4815162342
1
@adray 你能给我们展示一个这方面的例子吗?我也看不到。 - Keith

2
默认的Python实现采用垃圾回收和引用计数两种技术。在第一个示例中,文件对象的引用计数降为零,因此它会在垃圾回收器运行之前立即关闭。
第二个版本等效于这个:
_ = open('file')

由于文件仍被_引用,因此它在运行另一个命令之前仍然存在。

请注意,此行为仅适用于CPython。其他实现,如IronPython可能不会那么快地关闭文件,因此您应该在使用完文件后立即关闭它们。使用with语句是一种不错的方法。

with open('file') as f:
    data = f.read()

相关


这里需要指出的是,在第一种情况下,并没有返回文件描述符对象,而是返回了文件的内容。由于Python解释器允许您通过>>> _获取上一个命令的结果,因此第二种情况仍然可以使用文件描述符的引用。 - mklauber

2

这是因为您使用的交互式解释器会隐式保留对最后一个返回对象的引用。该引用被命名为_

Python2> open("/etc/hosts") 
<open file '/etc/hosts', mode 'r' at 0xf2c390> 
Python2> _ 
<open file '/etc/hosts', mode 'r' at 0xf2c390>

所以当您查看时,它仍然是“活着的”。做些其他事情:
Python2> max(0,1)
1

现在文件已关闭,因为它不再被引用。

但这是一个很好的例子,说明为什么你应该明确地关闭你真正想关闭的文件。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接