【问题标题】:Memory error when processing files in Python在 Python 中处理文件时出现内存错误
【发布时间】:2015-04-15 03:35:24
【问题描述】:

我打算根据每行中的键将一个总共约500MB的文件读入一个dict。代码sn-p如下:

f2 = open("ENST-NM-chr-name.txt", "r")   # small amount
lines = [l.strip() for l in f2.readlines() if l.strip()]
sample = dict([(l.split("\t")[2].strip("\""), l) for l in lines])    ## convert [(1,2), (3,4)] to {1:2, 3:4}

在内存为 4GB 的机器上运行时,python 会报内存错误。如果我将sample 变量的求值表达式更改为[l for l in lines],它可以正常工作。

一开始,我以为是split方法消耗了很多内存,所以我把我的代码调整成这样:

def find_nth(haystack, needle, n):
    start = haystack.find(needle)
    while start >= 0 and n > 1:
        start = haystack.find(needle, start+len(needle))
        n -= 1
    return start

...

sample = dict([(l[find_nth(l, "\t", 4):].strip(), l) for l in lines])

但结果是一样的。

一个新的发现是它可以在没有OOM的情况下正常运行,只要我删除dict()转换而不考虑代码逻辑。

谁能给我一些关于这个问题的想法?

【问题讨论】:

  • 这个网站上的某个地方是关于dict 占用多少内存的问题,这比您预期的要多得多。
  • 您能否给出与您提到的内容相关的具体网址链接?谢谢。 @MarkRansom
  • 如果我能记住它,我早就这么做了。对不起。
  • 另外,您是否在阅读制表符分隔值?

标签: python


【解决方案1】:

您将创建一个包含每一行的列表,该列表将继续存在,直到 lines 超出范围,然后基于它创建另一个完全不同的字符串的大列表,然后在之前创建一个 dict它可能会内存不足。只需一步构建dict

with open("ENST-NM-chr-name.txt") as f:
    sample = {}

    for l in f:
        l = l.strip()

        if l:
            sample[l.split("\t")[2].strip('"')] = l

您可以通过使用生成器表达式而不是列表推导来实现大致相同的效果,但(对我而言)感觉更好,而不是 strip 两次。

【讨论】:

  • 我删除了显式的r 模式,因为它是默认模式。
  • 我同意,关于不重复 strip。另一种选择是 map(str.strip, f)(在 Python 3 中)或 itertools.imap(…)(在 Python 2 中)。
【解决方案2】:

如果你把你的列表变成一个生成器,你的 dict 变成一个可爱的字典理解

f2 = open("ENST-NM-chr-name.txt", "r")   # small amount
lines = (l.strip() for l in f2 if l.strip())
sample = {line.split('\t')[2].strip('\"'): line for line in lines}

上面的第 2 行是错误的lines = (l.strip() for l in f2.readlines() if l.strip())

生成器和字典理解可能(以某种方式)减轻内存需求吗?

【讨论】:

  • 这并不能解决(可能)将整个文件读入内存的问题(无论如何这都是不必要的浪费)。 minitech 的回答避免了这种情况。
  • 不使用生成器来读取行吗?因为它没有在内存中构建行列表?
  • 你没有使用生成器,你的f2.readlines():这会将整个文件放入内存中(因为它构建了一个包含所有行的list)。大多数时候,readlines() 是可以避免的。即使您在这里通过执行for l in f2(其中 f2 是一个迭代器)来避免它,您仍然会将所有行存储在您的lines 中。同样,minitech 的答案是可行的方法(尽可能减少内存占用)。
  • @EOL: lines = (l.strip() for l in f2 if l.strip()) 将使它成为一个惰性求值的生成器。 (注意括号而不是方括号。)
  • 糟糕,我的意思是删除 readlines()... 并按照 minitech 的评论删除 l in f2
猜你喜欢
  • 1970-01-01
  • 2018-09-15
  • 1970-01-01
  • 2011-12-08
  • 2012-11-09
  • 2017-07-15
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多