Python writelines()和write()巨大的时差

时间:2017-06-15 06:56:06

标签: python performance file file-writing

我正在编写一个脚本,用于读取文件夹(每个文件大小从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.')

在使用90 MB文件(约900,000行)运行此代码时,它会打印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()的文件写入时间差异很大,即使数据相同?这是正常行为还是我的代码中有问题?两种情况下的输出文件似乎相同,所以我知道数据没有丢失。

3 个答案:

答案 0 :(得分:41)

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

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

您传入的是一个大字符串,字符串也是一个可迭代的字符串。在迭代时,您会得到个别字符,长度为1的字符串。因此,您实际上会len(data)file.write()进行单独调用。这很慢,因为你一次只能为一个字符构建一个写缓冲区。

不要将单个字符串传递给file.writelines()。传入列表或元组或其他可迭代的。

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

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

现在,如果您可以使clean_data()成为生成器,产生清理的行,您可以通过数据清理生成器从输入文件流式传输数据,然后输出到输出文件使用的内存不超过读取和写入缓冲区所需的内存,但是需要很多状态来清理行:

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

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

答案 1 :(得分:6)

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

只需将生成器理解传递给writelines,最后添加换行:没有不必要的内存分配,也没有循环(除了理解之外)

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

答案 2 :(得分:2)

'写(ARG)' method期望string作为参数。所以一旦它调用,它将直接写入。这就是它快得多的原因。 就像你使用writelines()方法一样,它需要字符串列表作为迭代器。因此,即使您向writelines发送数据,它也会假定它获得迭代器并尝试迭代它。因为它是一个迭代器,所以需要一些时间来迭代并编写它。

这是清楚的吗?