python 个列表列表的内存消耗

Memory consumption of python lists of lists

我最近处理的一个代码被发现使用了大约 200MB 到 运行 的内存,我很困惑为什么它需要那么多。

基本上它将一个文本文件映射到一个列表中,其中文件中的每个字符都是它自己的列表,其中包含该字符以及它到目前为止(从零开始)显示为它的两个项目的频率。

所以 'abbac...' 将是 [['a','0'],['b','0'],['b','1'],['a','1'],['c','0'],...]

对于一个100万个字符长的文本文件,它使用了200MB。

这是合理的还是我的代码在做其他事情?如果合理,是不是因为榜单数量多? [a,0,b,0,b,1,a,1,c,0...] 占用的空间会大大减少 space 吗?

当谈到内存使用和列表时,减少内存使用的最佳方法之一就是完全避免使用列表 - Python 对生成器形式的迭代器有很好的支持。如果你可以生成一个生成器而不是构建一个列表,你应该能够用很少的内存使用来做这样的事情。当然,这取决于您之后对数据的处理方式(假设您正在将此结构写入文件,您可以逐个进行,而不是一次存储整个内容)。

from collections import Counter

def charactersWithCounts():
    seen = Counter()
    for character in data:
        yield (character, seen[character])
        seen[character] += 1

如果您不需要列表本身,那么我完全支持@Lattyware 使用生成器的解决方案。

但是,如果这不是一个选项,那么也许您可以通过仅存储文件中每个字符的位置来压缩列表中的数据而不会丢失信息。

import random
import string

def track_char(s):
    # Make sure all characters have the same case
    s = s.lower()
    d = dict((k, []) for k in set(s))
    for position, char in enumerate(s):
         d[char].append(position)
    return d

st = ''.join(random.choice(string.ascii_uppercase) for _ in range(50000))
d = track_char(st)

len(d["a"])

# Total number of occurrences of character 2
for char, vals in d.items():
    if 2 in vals:
         print("Character %s has %s occurrences" % (char,len(d[char]))
Character C has 1878 occurrences

# Number of occurrences of character 2 so far
for char, vals in d.items():
    if 2 in vals:
        print("Character %s has %s occurrences so far" % (char, len([x for x in d[char] if x <= 2))
Character C has 1 occurrences so far

这样就不需要在每次出现的时候都复制字符串,维护所有出现的信息。

要比较原始列表或此方法的对象大小,这里有一个测试

import random
import string
from sys import getsizeof

# random generation of a string with 50k characters
st = ''.join(random.choice(string.ascii_uppercase) for _ in range(50000))

# Function that returns the original list for this string
def original_track(s):
    l = []
    for position, char in enumerate(s):
        l.append([char, position])
    return l

# Testing sizes
original_list = original_track(st)
dict_format = track_char(st)

getsizeof(original_list)
406496
getsizeof(dict_format)
1632

如您所见,dict_format 的大小大约缩小了 250 倍。然而,这种大小差异在较大的字符串中应该更为明显。