我需要将一个非常大的字典导入到python中,并且我遇到了一些意想不到的内存瓶颈。字典的格式为
d = {(1,2,3):(1,2,3,4), (2,5,6)=(4,2,3,4,5,6), ... }
所以每个键都是一个3元组,每个值都是一个相对较小的任意大小的元组(可能永远不会超过30个元素)。使字典变大的原因是键的数量。我正在使用的一个较小的例子大约有247257个键。我通过模拟生成这个字典,所以我可以写出一个定义这个字典的文本文件,对于我刚刚提到的例子,这是一个94MB的文件。我遇到的瓶颈是初始编译为python字节代码会占用大约14GB的内存。所以我第一次导入字典时,我看到RAM使用率上升了,经过10秒后,所有内容都被加载了。如果已生成.pyc文件,则导入几乎是即时的。使用pympler,我已经确定这个字典在内存中只有大约200 MB。这是什么交易?关于如何将此字典加载到python或至少编译为字节代码,我还有其他选项吗?我正在用C ++运行生成模拟,我无法以任何我需要的格式编写文件。那里有没有选项(python库等)?我正在与一些需要这些数据作为字典的软件接口,所以请不要在该领域提出其他建议。另外,万一你想知道,我已经在文本文件中定义了字典,就像上面的定义一样,
d = {}
d[1,2,3] = (1,2,3,4)
d[2,5,6] = (4,2,3,4,5,6)
...
两者都在编译到字节代码时给出相同的内存峰值。事实上,第二个似乎稍差,这对我来说是令人惊讶的。必须有一些方法来驯服初始编译需要的ram数量。看起来它应该能够以某种方式一次编译一个键值对。有什么想法吗?
其他信息: 使用python 2.6.5
答案 0 :(得分:3)
我想问题是,在解析文件时,会为每个元素添加一个巨大的语法树,但开销很小。生成字节码后,不再需要和转储语法树,从而产生200MB的数据。
您是否尝试使用以下格式将数据存储在单独的文件中,然后在python中动态加载?
1,2,3=1,2,3
2,5,6=4,2,3,4,5,6
Python脚本应如下所示:
file = open("filename")
d = {}
for line in file:
key, val = line.split("=")
key = tuple(key.split(","))
d[key] = tuple(val.split(","))
file.close()
答案 1 :(得分:2)
答案 2 :(得分:1)
我猜你在执行“import module_containing_humungous_dict_statement”时会出现大的编译峰值。然后,如果您只有一个语句或247257个单独的赋值语句,则无关紧要,整个模块仍将立即编译。您可以尝试使用单独的赋值语句表单,然后打开文件,一次读取一行,然后执行它。那么你一次只会编译一行。可能需要一段时间。
答案 3 :(得分:0)
我怀疑创建要用作密钥的列表是多么昂贵。定义一个函数,它将三元组的三个部分作为输入并返回一个管道分隔的字符串。用它作为你的钥匙。
答案 4 :(得分:0)
我读你的问题的方式是,你在你的模拟器中生成Python源,并且生成的源具有硬编码的巨型字典的内容。如果这是真的,那么你可以很容易地生成这个:
def giantdict():
d0 = {(1, 2): (3, 4), (3, 4): (5, 6), ...} # first 1000 key/value pairs here
d1 = {(1, 2): (3, 4), (3, 4): (5, 6), ...} # next 1000 key/value pairs
d2 = {(1, 2): (3, 4), (3, 4): (5, 6), ...} # next 1000 key/value pairs
d3 = {(1, 2): (3, 4), (3, 4): (5, 6), ...} # next 1000 key/value pairs
# ... until you're done
bigd = d0
bigd.update(d1)
del d1
bigd.update(d2)
del d2
# ... continue updating with all the dN dictionaries
return bigd
我不确定这会改善编译时间,但是尝试一下。如果在编译时将所有内容放在一个数据结构中会受到惩罚,那么将其拆分并在运行时组装这些部分可能会解决它。
虽然这种代码(我的或你的代码)会引起我的愤怒和愤怒,如果一个人写下它,我认为没有必要生成的代码“好”,只要你知道没有人需要阅读或维护它。
答案 5 :(得分:0)
这是一个使用defaultdict自动嵌套索引值的类,使用一些特殊的__getitem__
和__setitem__
方法接受元组作为参数:
from collections import defaultdict
defdict3level = (lambda : defaultdict(lambda :
defaultdict( lambda :
defaultdict(tuple))))
class dict3level(object):
def __init__(self):
self.defdict = defdict3level()
def __getitem__(self, key):
if isinstance(key, tuple):
if len(key)==3:
return self.defdict[key[0]][key[1]][key[2]]
elif len(key)==2:
return self.defdict[key[0]][key[1]]
elif len(key)==1:
return self.defdict[key[0]]
else:
return self.defdict[key]
def __setitem__(self, key, value):
if isinstance(key, tuple) and len(key)==3:
self.defdict[key[0]][key[1]][key[2]] = value
else:
self.defdict[key] = value
def __getattr__(self, attr):
return getattr(self.defdict, attr)
现在执行你之前的所有作业:
d = dict3level()
d[1,2,3] = (1,2,3,4)
d[1,2,7] = (3,4,5,6)
d[2,5,6] = (4,2,3,4,5,6)
您仍然可以获得特定元组的特定条目:
# get a specific entry
print d[1,2,3]
但你也可以按级别导航你的词典:
# get all different 0'th index values
print d.keys()
# get all sub values in d[1,2,*]
print d[1,2].keys()
for key in d[1,2]:
print "d[1,2,%d] = %s" % (key, d[1,2][key])
# no such entry, return empty tuple
print d[1,2,0]
给出:
print d[1,2,3] -> (1, 2, 3, 4)
print d.keys() -> [1, 2]
print d[1,2].keys() -> [3, 7]
for key in d[1,2]:... ->
d[1,2,3] = (1, 2, 3, 4)
d[1,2,7] = (3, 4, 5, 6)
print d[1,2,0] -> ()
(不知道这会如何影响你的记忆和/或酸洗问题,但最终的结构有更多的能力。)
答案 6 :(得分:0)