读取整个文件是否会使文件句柄保持打开状态?


372

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

Answers:


585

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

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

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

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

确保该文件已关闭的一种更好的解决方案是以下模式:

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

块结束后,它将始终立即关闭文件;即使发生异常。

编辑:在上面提出一个更好的点:

除了file.__exit__(),这是在with上下文管理器设置中“自动”调用的,唯一file.close()可以自动调用的其他方法(即,除了自己明确调用之外)是via 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种不同的垃圾回收实现


24
一段时间以来,实际上并没有其他Python实现。但是依靠实现细节并不是真正的Pythonic。
Karl Knechtel

它仍然是特定于实现的,还是已经标准化的?__exit__()在这种情况下不打电话听起来像是设计缺陷。
rr-

2
@jgmjgm正是由于这3个问题,GC不可预测,try/ finally轻巧并且with解决了清理程序的高度普遍的无用。“显式关闭”和“使用……进行管理with” 之间的区别在于,即使抛出异常,也会调用出口处理程序。您可以将close()in 放在finally子句中,但这与使用它没有太大区别with,有点混乱(3条额外的行而不是1条),并且更难于正确使用。
SingleNegationElimination

1
我不明白的是为什么“ with”会更可靠,因为它也不是很明确。是因为规范说它必须始终按这样执行吗?
jgmjgm

3
@jgmjgm更加可靠,因为with foo() as f: [...]它与,,[...] 基本相同f = foo()f.__enter__()并且f.__exit__() 处理了异常,因此__exit__始终调用。因此,该文件始终会关闭。
neingeist

104

您可以使用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()

2

好吧,如果您必须逐行读取文件才能使用每一行,则可以使用

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

0

与其将文件内容作为单个字符串检索,不如将其存储为文件包括的所有行的列表,这很方便:

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

可以看出,一个需要经连结的方法添加.strip().split("\n")这个线程的主要答案

在这里,.strip()只需删除整个文件字符串末尾的空格和换行符,并.split("\n")通过在每个换行符 \ n处拆分整个文件字符串来生成实际列表。

而且,这种方式可以将整个文件内容存储在一个变量中,这在某些情况下可能是需要的,而不是像在文件中指出的那样逐行循环文件。 先前的答案中

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.