itertools.islice和读取功能之间的区别

时间:2019-05-19 09:04:17

标签: python memory-management file-handling itertools

我试图更好地了解如何以最小的内存使用量处理具有数百万条记录的文件。

为了练习,我创建了一个约650万行的文件,并编写了几个函数将其拆分为约7个文件,每个文件有100万行。在第一个函数中,我使用了python文件读取方法来创建逻辑,以便在读取100万行之后创建一个新文件,直到找到最后写入500K行的最后一个文件为止。

该功能永远需要运行。

然后,我创建了另一个函数,使用itertools.islice拆分文件。运行大约不到2秒的时间。

现在,我知道islice是一个迭代文件对象的迭代器,因此期望它具有更高的内存效率。但是,它与read()方法有何不同?

我认为甚至read()都逐一遍历文件中的每一行(有点像迭代器..)。因此,我期望两个程序的性能相似。你们能帮我理解为什么Islice这么快吗?

这都是两段代码-

1使用read()-

with open("bigfile.txt","r") as f:
    filenum = 1
    j = 1
    for i, line in enumerate(f):
        if j <= 1000000:
            with open("big_out_%d" % filenum, "a") as outfile:
                outfile.write(line)
        j += 1
        if j == 1000000:
            j = 1
            filenum += 1
            with open("big_out_%d" % filenum, "a") as outfile:
                outfile.write(line)

2使用islice-

import itertools
import time

start = time.time()

with open("bigfile.txt","r") as f:
    i = 1
    while True:
        chunk = list(itertools.islice(f, 1000000))
        if not chunk:
            print "reached the end"
            break
        with open("out%d.txt" % i, "w") as out:
            out.writelines(chunk)
        print i
        i += 1

end = time.time()
print "time is %d" % ((end-start))

2 个答案:

答案 0 :(得分:3)

区别与islice和read()没有关系。您的两个程序的逻辑差异很大。

在第一个清单中,循环遍历文件的各行。在循环的每次迭代中,您都将打开文件,写一行,然后再次关闭文件。 (“ with open”语法使文件在with:块的末尾关闭)。在完成时,您已经将6500000行写入了七个不同的文件,但是您还执行了6500000文件打开和6500000文件关闭的操作。我对操作系统不能有效地做到这一点感到惊讶。

在第二个清单中,您读取了1000000行的块,然后将所有这些写一次。您仍然编写6500000行,但是在这里执行7次打开和7次关闭。根本不一样。

与:一起使用:对于输出文件,您的第一份清单非常笨拙。试试这个:

with open("bigfile.txt","r") as f:
    filenum = 1
    j = 1
    outfile = open("big_out_%d" % filenum, "w")
    try:
        for line in f:
            outfile.write(line)
            j += 1
            if j == 1000000:
                outfile.close()
                j = 1
                filenum += 1
                outfile = open("big_out_%d" % filenum, "w")
    finally:
        outfile.close()

我没有测试此代码。如果有错误,应该很容易解决。

通过这种方法,您一次加载到内存中永远不会超过一行。

答案 1 :(得分:0)

该代码的第一个版本打开并关闭它写入的每一行的输出文件。这将非常慢,因为它将每次将缓冲区刷新到磁盘。仅使文件在两行之间保持打开状态可能是第二个版本的最大提速(尽管连续读取和写入许多行可能会带来适度的额外好处,只要您可以同时将所有行都保留在内存中即可)。 / p>

您可以尝试使用该代码的第三个版本,这是当前两个版本之间的某种方式。它仍然读取和写入单行,但在两次写入之间保持输出文件打开:

with open("bigfile.txt","r") as f:
    outfile = None
    for i, line in enumerate(f):
        if i % 1000000 == 0:
            if outfile:
                outfile.close()
            outfile = open("big_out_%d" % (i // 1000000), "w")
        outfile.write(line)
    if outfile:
        outfile.close()

请注意,我通过使用i中的索引enumerate简化了所有操作,而不是手动更新第一个filenumj个整数,从而简化了操作码。这不太可能对性能产生重大影响,但是会使代码更好。