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

Arj*_*ind 30 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.')
Run Code Online (Sandbox Code Playgroud)

在使用90 MB文件(~900,000行)运行此代码时,它将打印140秒作为写入文件所需的时间.在这里我用过writelines().所以我搜索了不同的方法来提高文件写入速度,在我阅读的大多数文章中,它说write()并且writelines()不应该显示任何差异,因为我正在编写单个连接字符串.我还检查了以下声明所花费的时间:

new_string = '\n'.join(new_my_list) + '\n'
Run Code Online (Sandbox Code Playgroud)

它只花了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.')
Run Code Online (Sandbox Code Playgroud)

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

Mar*_*ers 44

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

def writelines(self, lines)
    for line in lines:
        self.write(line)
Run Code Online (Sandbox Code Playgroud)

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

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

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

 myWrite.writelines(line + '\n' for line in new_my_list)
Run Code Online (Sandbox Code Playgroud)

现在,如果你可以创建clean_data()一个生成器,产生清理的行,你可以从输入文件,数据清理生成器流出数据,然后输出到输出文件,而不使用读取和写入缓冲区所需的更多内存.清理你的线条需要很多状态:

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

另外,我会考虑更新clean_data()以发出包含换行符的行.


Jea*_*bre 7

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

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

myWrite.writelines("{}\n".format(x) for x in my_list)
Run Code Online (Sandbox Code Playgroud)