"file.readlines()"、"list(file)" 和 "file.read().splitlines(True)" 之间有区别吗?

Is there a difference between : "file.readlines()", "list(file)" and "file.read().splitlines(True)"?

有什么区别:

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)吗?

显式比隐式好,所以我更喜欢:

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)

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 文档中允许并推荐所有语法。

所以,我的建议是:

首先,为您的特定情况选择最合乎逻辑的,然后选择您认为最可读在整个代码中与它保持一致

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

文件对象

An object exposing a file-oriented API (with methods such as read() or write()). Depending on the way it was created, a file object can mediate access to a real on-disk file or to another type of storage or communication device (for example standard input/output, in-memory buffers, sockets, pipes, etc.). File objects are also called file-like objects or streams. The canonical way to create a file object is by using the open() function. 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()

The method readlines() reads until EOF using readline() and returns a list containing the lines.

与列表的区别:

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

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

阅读

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

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

f 这是一个类似文件的对象,正在通过 list 迭代文件中的 returns 行。


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

f.read() returns 一个字符串,您在换行处拆分它,返回一个字符串列表。


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

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

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

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

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

至于 list(f)f.readlines(),两者之间没有具体的优势;他们之间的选择取决于风格和品味。