如果您使用 content = open('Path/to/file', 'r').read()
读取整个文件,文件句柄是否保持打开状态,直到脚本退出?有没有更简洁的方法来读取整个文件?
这个问题的答案在某种程度上取决于特定的 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 has 6 different garbage collection implementations!
您可以使用 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
implementation:
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")
添加到 the main answer in this thread。
在这里,.strip()
只是删除了整个文件字符串结尾的空格和换行符,而 .split("\n")
通过在每个 换行符 \n处拆分整个文件字符串来生成实际列表。
此外,通过这种方式,整个文件内容可以存储在一个变量中,这在某些情况下可能是需要的,而不是像 this previous answer 中指出的那样逐行循环文件。
不定期副业成功案例分享
__exit__()
听起来像是设计缺陷。try
/finally
很繁琐,以及with
解决的清理处理程序非常常见的无用处。 “显式关闭”和“使用with
管理”之间的区别在于即使抛出异常也会调用退出处理程序。您可以将close()
放在finally
子句中,但这与使用with
并没有太大区别,有点混乱(3 行而不是 1 行),而且更难做到恰到好处。with foo() as f: [...]
与f = foo()
、f.__enter__()
、[...] 和f.__exit__()
处理异常基本相同,因此始终调用__exit__
.所以文件总是被关闭。