Python writelines()和write()时间差很大

2024-04-19 09:38:06 发布

您现在位置:Python中文网/ 问答频道 /正文

我正在写一个脚本,它读取一个文件夹中的文件(每个文件夹的大小从20 MB到100 MB不等),修改每行中的一些数据,然后写回文件的副本。

with open(inputPath, 'r+') as myRead:
     my_list = myRead.readlines()
     new_my_list = clean_data(my_list)
with open(outPath, 'w+') as myWrite:
     tempT = time.time()
     myWrite.writelines('\n'.join(new_my_list) + '\n')
     print(time.time() - tempT)
print(inputPath, 'Cleaning Complete.')

在用一个90mb文件(大约900000行)运行此代码时,它打印140秒作为写入文件所需的时间。在这里我使用了writelines()。所以我寻找了不同的方法来提高文件写入速度,在我阅读的大多数文章中,它都说write()writelines()不应该显示任何差异,因为我正在编写一个连接字符串。我还检查了所花费的时间,仅用于以下语句:

new_string = '\n'.join(new_my_list) + '\n'

它只花了0.4秒,所以花费的大量时间并不是因为创建了列表。 为了尝试write()我尝试了以下代码:

with open(inputPath, 'r+') as myRead:
     my_list = myRead.readlines()
     new_my_list = clean_data(my_list)
with open(outPath, 'w+') as myWrite:
     tempT = time.time()
     myWrite.write('\n'.join(new_my_list) + '\n')
     print(time.time() - tempT)
print(inputPath, 'Cleaning Complete.')

打印了2.5秒。为什么即使是相同的数据,write()writelines()的文件写入时间有如此大的差异?这是正常行为还是我的代码有问题?两种情况下的输出文件似乎都是相同的,所以我知道数据不会丢失。


Tags: 文件newwritelinestimemyaswith时间
3条回答

“write(arg)”方法需要字符串作为其参数。所以一旦它调用,它就会直接写。这就是它更快的原因。 如果您使用的是writelines()方法,则它需要字符串列表作为迭代器。因此,即使您正在向writelines发送数据,它也假设它得到了迭代器,并试图对其进行迭代器。因此,由于它是一个迭代器,因此需要一些时间来迭代和编写它。

明白了吗?

作为Martijn答案的补充,最好的方法是首先避免使用join构建列表

只需将生成器理解传递给writelines,在末尾添加新行:没有不必要的内存分配和循环(除了理解之外)

myWrite.writelines("{}\n".format(x) for x in my_list)

file.writelines()需要字符串的iterable。然后继续循环并为iterable中的每个字符串调用file.write()。在Python中,该方法执行以下操作:

def writelines(self, lines)
    for line in lines:
        self.write(line)

您传入的是一个大字符串,字符串也是一个iterable字符串。迭代时,您将得到个字符,长度为1的字符串。所以实际上,您正在对len(data)进行单独的调用。这很慢,因为您一次只能构建一个字符的写缓冲区。

不要将单个字符串传递给file.writelines()。改为传入列表、元组或其他iterable。

可以通过在生成器表达式中添加换行符来发送单独的行,例如:

 myWrite.writelines(line + '\n' for line in new_my_list)

现在,如果您可以生成clean_data()a生成器,生成已清理的行,那么您可以从输入文件、通过数据清理生成器流式传输数据,并输出到输出文件,而无需使用比读写缓冲区所需更多的内存,而且无论需要多少状态来清理行:

with open(inputPath, 'r+') as myRead, open(outPath, 'w+') as myWrite:
    myWrite.writelines(line + '\n' for line in clean_data(myRead))

此外,我会考虑更新clean_data()以发出包含新行的行。

相关问题 更多 >