读取整个文件是否会使文件句柄打开?

如果你用content = open('Path/to/file', 'r').read()读取整个文件,文件句柄是否一直打开到脚本退出?是否有更简洁的方法来读取整个文件?

484680 次浏览

这个问题的答案在某种程度上取决于特定的Python实现。

要理解这一切,请特别注意实际的file对象。在代码中,该对象仅在表达式中被提及一次,并且在read()调用返回后立即不可访问。

这意味着文件对象是垃圾。剩下的唯一问题是“垃圾收集器什么时候收集文件对象?”

在使用引用计数器的CPython中,这种垃圾会立即被注意到,因此它会立即被收集。其他python实现通常不是这样。

一个更好的解决方案,确保文件是关闭的,是这样的模式:

with open('Path/to/file', 'r') as content_file:
content = content_file.read()

它总是在块结束后立即关闭文件;即使出现异常。

编辑:说得更清楚点:

除了在with上下文管理器设置中“自动”调用file.__exit__()之外,自动调用file.close()的唯一其他方式(即,除了显式地自己调用它之外)是通过file.__del__()。这就引出了一个问题:__del__()什么时候被调用?

一个正确编写的程序不能假定终结器将在程序终止之前的任何时候运行。

——https://devblogs.microsoft.com/oldnewthing/20100809-00/?p=13203

特别是:

对象从不显式地销毁;然而,当它们变得不可访问时,它们可能会被垃圾回收。实现允许延迟垃圾收集或完全忽略它 -如何实现垃圾收集是一个实现质量的问题,只要没有对象仍然是可达的被收集。

[…]

CPython目前使用一种引用计数方案(可选)延迟检测循环链接垃圾,一旦它们变得不可达,它就会收集大多数对象,但不保证收集包含循环引用的垃圾。

——https://docs.python.org/3.5/reference/datamodel.html#objects-values-and-types

(强调我的)

但正如它所表明的,其他实现可能有其他行为。例如,PyPy 6有不同的垃圾收集实现!

你可以使用pathlib

对于Python 3.5及以上版本:

from pathlib import Path
contents = Path(file_path).read_text()

对于较旧版本的Python,请使用pathlib2:

$ pip install pathlib2

然后:

from pathlib2 import Path
contents = Path(file_path).read_text()

这是实际的read_text 实现:

def read_text(self, encoding=None, errors=None):
"""
Open the file in text mode, read it, and close the file.
"""
with self.open(mode='r', encoding=encoding, errors=errors) as f:
return f.read()

如果您必须一行一行地读取文件来处理每一行,您可以使用

with open('Path/to/file', 'r') as f:
s = f.readline()
while s:
# do whatever you want to
s = f.readline()

或者更好的方式:

with open('Path/to/file') as f:
for line in f:
# do whatever you want to

不再以单个字符串的形式检索文件内容, 将内容存储为文件包含的所有行的列表:

with open('Path/to/file', 'r') as content_file:
content_list = content_file.read().strip().split("\n")

可以看出,需要将串联的方法.strip().split("\n")添加到这篇文章的主要答案

这里,.strip()只是删除整个文件字符串结尾的空格和换行符, 和.split("\n")通过在每个换行符 \ n处拆分整个文件字符串来生成实际的列表 此外,< p > 这样,整个文件内容可以存储在一个变量中,这在某些情况下可能是需要的,而不是像之前的答案中指出的那样逐行遍历文件