是否有区别:“file.readlines()”、“list(file)”和“file.read().splitlines(True)”?

Posted

技术标签:

【中文标题】是否有区别:“file.readlines()”、“list(file)”和“file.read().splitlines(True)”?【英文标题】:Is there a difference between : "file.readlines()", "list(file)" and "file.read().splitlines(True)"? 【发布时间】:2018-12-30 23:06:19 【问题描述】:

有什么区别:

with open("file.txt", "r") as f:
    data = list(f)

或者:

with open("file.txt", "r") as f:
    data = f.read().splitlines(True)

或者:

with open("file.txt", "r") as f:
    data = f.readlines()

它们似乎产生完全相同的输出。 一个比另一个更好(或更 Pythonic)吗?

【问题讨论】:

Ofc,我先试了。它们产生完全相同的输出。这就是为什么我问是否有任何区别......(为清楚起见添加了一个小编辑)。 最大的问题是你为什么需要这份清单。如果你最终要迭代它,最pythonic的事情就是永远不要构建它,而是迭代文件的行。 【参考方案1】:

显式优于隐式,所以我更喜欢:

with open("file.txt", "r") as f:
    data = f.readlines()

但是,如果可能的话,最pythonic的就是直接使用文件迭代器,而不是将所有内容加载到内存中,例如:

with open("file.txt", "r") as f:
    for line in f:
       my_function(line)

【讨论】:

我认为在这种情况下我不能使用迭代器。我需要从文件中读取第一行,使用它并用它做一些事情。比从文件中删除第一行,使第二行成为第一行,依此类推。我正在考虑使用其中之一,而不是使用 data = data[1:] 并在文件中写回。 @Bermuda:firstline = next(f)。然后用它做事。然后with open("file.txt.temp", "r") as f2: f2.write(f.read())。然后将file.txt.temp 移到file.txt 上。 这行得通,这正是我所需要的!这非常好......但我不明白它是如何工作的。根据文档, next() 从迭代器中检索下一项。没问题。但是为什么 f2.write(f.read()) 之后,第一行就消失了? f.read() 是否与 next() 共享相同的迭代器,因此从该点开始读取? @StevenRumbalski 这是实现他想要的一个非常好的方法。我只是认为这完全偏离了他的问题的目的。他可能应该问另一个问题,以便您可以发布您提出的方法。就个人而言,我不知道如何处理这个问题。但考虑到他是如何提出问题以及这是一条评论这一事实,我不知道未来的用户会如何找到这个答案。 @StevenRumbalski 这是一个开放线程,供您回答***.com/q/51481747/7692463。如果您认为我可以改进问题的措辞,请随时给我反馈。【参考方案2】:

TL;DR;

考虑到您之后需要一个列表来操作它们,您提出的三个解决方案在语法上都是有效的。没有更好(或更 Pythonic) 的解决方案,尤其是它们都被官方 Python documentation 推荐。因此,请选择您认为最易读并在整个代码中保持一致的那个。如果性能是决定性因素,请参阅下面我的timeit 分析。


这是timeit(10000 个循环,test.txt 中的 ~20 行),

import timeit

def foo():
    with open("test.txt", "r") as f:
        data = list(f)

def foo1():
    with open("test.txt", "r") as f:
        data = f.read().splitlines(True)

def foo2():
    with open("test.txt", "r") as f:
        data = f.readlines()

print(timeit.timeit(stmt=foo, number=10000))
print(timeit.timeit(stmt=foo1, number=10000))
print(timeit.timeit(stmt=foo2, number=10000))

>>>> 1.6370758459997887
>>>> 1.410844805999659
>>>> 1.8176437409965729

我尝试了多个循环和行,f.read().splitlines(True) 的性能似乎总是比其他两个好一点。

现在,从句法上讲,您的所有示例似乎都是有效的。有关更多信息,请参阅此documentation。

据此,如果您的目标是从文件中读取行,

for line in f:
    ...

他们声称它内存效率高、速度快并且代码简单如果您不需要在列表中操作它们,这将是另一个不错的选择。

编辑

请注意,您无需将 True 布尔值传递给 splitlines。默认情况下,它具有您想要的行为。

我的个人建议

我不想让这个答案过于基于意见,但我认为这对你来说是有益的,我认为性能不应该是你的决定因素,直到它实际上对你来说是一个问题。特别是因为在我链接的官方 Python 文档中允许并推荐所有语法。

所以,我的建议是:

首先,根据您的具体情况选择最合乎逻辑的,然后选择您认为最易读保持一致的那个贯穿你的代码。

【讨论】:

谢谢你,如果唯一的区别是风格,是的,更好的性能总是好的:) @Bermuda 确实,但请注意,您还应该尝试在您的特定计算机上使用timeit 以查看似乎最有效的方法。只是出于好奇,试试我的代码,然后就你的计算机上最好的代码回复我。 与您的分析相关,test.txt 包含多少行?文件有多大? @MichaelMior 我通过指定行数来编辑问题,但如答案中所述,我还尝试了多个文件大小和循环数。至少从我能够测试的情况来看,f.read().splitlines(True) 的表现更好。您也许可以确认您有类似的行为。 @scharette 感谢分享。我会犹豫从一个文件中只有 20 行的测试中得出任何结论,但我同意这可能是真的,没有太大的差异。【参考方案3】:

您的所有三个选项都会产生相同的最终结果,但其中一个肯定比其他两个更糟糕:执行f.read().splitlines(True)

这是最糟糕的选择的原因是它需要最多的内存。 f.read() 将文件内容作为单个(可能是巨大的)字符串对象读入内存,然后在其上调用 .splitlines(True) 会另外创建各个行的列表,然后只有在此之后才会获得包含文件全部内容的字符串对象垃圾收集并释放其内存。所以,在内存使用高峰的时刻——就在大字符串的内存被释放之前——这种方法需要足够的内存来将文件的全部内容存储在内存中两次——一次作为字符串,一次作为字符串数组。

相比之下,list(f)f.readlines() 将从磁盘读取一行,将其添加到结果列表中,然后读取下一行,依此类推。因此,整个文件内容永远不会在内存中重复,因此峰值内存使用量约为.splitlines(True) 方法的一半。因此,这些方法优于使用.read().splitlines(True)

至于list(f)f.readlines(),它们中的任何一个都没有明显的优势;它们之间的选择是风格和品味的问题。

【讨论】:

【参考方案4】:

在这 3 种情况下,您使用 context manager 来读取文件。这个文件是file object

文件对象

公开面向文件的 API 的对象(使用 read() 或 写())。根据创建的方式,文件对象可以调解对真实磁盘文件或其他类型的存储或通信设备(例如标准输入/输出、内存缓冲区、套接字、管道等)的访问。 .文件对象也称为类文件对象或流。 创建文件对象的规范方法是使用 open() 函数。 https://docs.python.org/3/glossary.html#term-file-object

列表

with open("file.txt", "r") as f:
    data = list(f)

这是有效的,因为您的文件对象是一个类似对象的流。转换为列表的工作大致如下:

[element for element in generator until I hit stopIteration]

readlines 方法

with open("file.txt", "r") as f:
    data = f.readlines()

方法 readlines() 使用 readline() 读取直到 EOF 并返回一个 包含行的列表。

与列表的区别:

    您可以指定要读取的元素数量:fileObject.readlines( sizehint )

    如果存在可选的 sizehint 参数,而不是读取到 EOF,而是读取总计大约 sizehint 字节的整行(可能在向上舍入到内部缓冲区大小之后)。

阅读

When should I ever use file.read() or file.readlines()?

【讨论】:

【参考方案5】:

它们都实现了返回字符串列表但使用不同方法的相同目标。 f.readlines() 是最 Pythonic 的。

with open("file.txt", "r") as f:
    data = list(f)

f 这里是一个类似文件的对象,它通过list 进行迭代,返回文件中的行。


with open("file.txt", "r") as f:
    data = f.read().splitlines(True)

f.read() 返回一个字符串,您将其拆分为换行符,返回一个字符串列表。


with open("file.txt", "r") as f:
    data = f.readlines()

f.readlines() 和上面一样,它读取整个文件并在换行符处分割。

【讨论】:

以上是关于是否有区别:“file.readlines()”、“list(file)”和“file.read().splitlines(True)”?的主要内容,如果未能解决你的问题,请参考以下文章

File.ReadLines 没有锁定它?

File.ReadLines 啥时候释放资源

day 1 作业

4.2Python文件基本操作2:tellseek

login未完成

Python_usetxt