Python 如何储存一本大字典?

Python 如何储存一本大字典?,python,dictionary,pickle,Python,Dictionary,Pickle,我有一个大字典(28MB)“MyDict”存储在MyDict.py文件中 如果我执行该语句: from MyDict import MyDict 将引发MemoryError异常 如何使用cPickle或shelve模块访问此词典 在不访问MyDict的情况下,如何将此MyDict.py文件写入cPickle或shelve 此MyDict是通过写入文件生成的。 以下是字典中的键值对: {"""ABCD""" : [[(u'2011-03-21', 35.5, 37.5, 35.3, 35.85

我有一个大字典(28MB)“MyDict”存储在
MyDict.py
文件中

如果我执行该语句:

from MyDict import MyDict
将引发
MemoryError
异常

如何使用
cPickle
shelve
模块访问此词典

在不访问MyDict的情况下,如何将此
MyDict.py
文件写入
cPickle
shelve

此MyDict是通过写入文件生成的。 以下是字典中的键值对:

{"""ABCD""" : [[(u'2011-03-21', 35.5, 37.5, 35.3, 35.85, 10434.0, 35.85), (u'2012-03-03', 86.0, 87.95, 85.55, 86.2, 30587.0, 86.2), (u'2011-03-23', 36.9, 36.9, 35.25, 36.1, 456.0, 36.1)],
    [(u'2011-03-18', 37.0, 38.0, 36.5, 36.5, 861.0, 36.5), (u'2012-03-03', 86.0, 87.95, 85.55, 86.2, 30587.0, 86.2), (u'2011-03-21', 35.5, 37.5, 35.3, 35.85, 10434.0, 35.85)],
    [(u'2011-03-16', 37.0, 37.9, 36.3, 36.7, 3876.0, 36.7), (u'2012-03-03', 86.0, 87.95, 85.55, 86.2, 30587.0, 86.2), (u'2011-03-21', 35.5, 37.5, 35.3, 35.85, 10434.0, 35.85)],
    [(u'2010-12-09', 40.5, 41.95, 36.3, 36.75, 42943.0, 36.75), (u'2011-10-26', 67.95, 71.9, 66.45, 70.35, 180812.0, 70.35), (u'2011-03-21', 35.5, 37.5, 35.3, 35.85, 10434.0, 35.85)],
    [(u'2009-01-16', 14.75, 15.0, 14.0, 14.15, 14999.0, 14.05), (u'2010-01-11', 50.0, 52.8, 49.0, 50.95, 174826.0, 50.95), (u'2009-01-27', 14.3, 15.0, 13.9, 14.15, 3862.0, 14.15)]]}

shelve
实际上是一个不错的选择。它的作用就像一个字典,但它由一个BDB(或类似的)键值数据库文件支持,Python将处理所有缓存等,因此它不需要一次将整个内容加载到内存中

下面介绍如何创建搁置文件。请注意,工具架键必须是字符串。还要注意,我是在原地创建shelf,而不是首先创建一个
dict
并将其搁置。这样你就避免了在内存中构建一个巨大的dict的成本,而这个巨大的dict从一开始就造成了问题

from contextlib import closing
import shelve

def makedict(shelf):
    # Put the real dict-generating code here, obviously
    for i in range(500000);
        shelf[str(i)] = i

with closing(shelve.open('mydict.shelf', 'c')) as shelf:
    makedict(shelf)
要使用它,不要实际阅读它;将其保留为磁盘架:

from contextlib import closing
import shelve

with closing(shelve.open('mydict.shelf')) as d:
    # Put all your actual work here.
    print len(d)
如果使用代码的词典不容易放入范围,请将
替换为
语句,并使用普通的
打开
,完成后显式地
关闭

pickle
可能不是一个好主意,因为你仍然需要将整个内容读入内存。与导入一个定义了巨大文本的模块相比,它可能会占用更少的临时内存,也可能占用更少的磁盘空间,但是,拥有如此巨大的内存哈希表仍然是一个问题。但是你可以随时测试它,看看它有多好

下面介绍如何创建pickle文件。请注意,您可以(几乎)使用任何您想要的键,而不仅仅是字符串。但是,您必须先构建整个
dict
,然后才能
pickle

import cPickle

def makedict():
    # Put the real dict-generating code here, obviously
    return {i:i for i in range(500000)}

with open('mydict.pickle', 'wb') as f:
    cPickle.dump(d, f, -1)
这将创建一个47MB的文件

现在,要在主应用程序中使用它:

import cPickle

def loaddict():
    with open('mydict.pickle', 'rb') as f:
        return cPickle.load(f)
pickle
的基本问题同样适用于任何其他必须保存和加载的持久性格式,无论是您自己编写的自定义格式,还是JSON或YAML之类的标准格式。(当然,如果你需要与其他程序的互操作性,特别是在其他语言中,像JSON这样的东西是最好的选择。)你最好使用数据库;唯一的问题是,什么样的数据库

anydbm
类型数据库的优点是,您可以像使用
dict
一样使用它,而不用担心如何加载/保存/访问它(除了
打开
关闭
行)。
anydbm
的问题在于它只允许将字符串映射到字符串

shelve
模块有效地包装了
anydbm
,并对每个值进行了酸洗。您的键仍然必须是字符串,但您的值几乎可以是任何内容。因此,只要您的键是字符串,并且没有从值到外部对象的任何引用,它就相当透明地替代了
dict

其他选项——
sqlite3
、各种现代nosql数据库等——要求您更改访问数据的方式,甚至更改数据的组织方式。(一个列表列表不是一个清晰的ER模型)当然,从长远来看,这可能会导致更好的设计,所以如果你认为你真的应该使用一个关系模型,考虑这个想法。
从评论中,@ekta想让我解释一下为什么对
dbm
shelve
存在一些限制

首先,
dbm
可以追溯到70年代。一个能够简单高效地将8位字符串映射到字符串的数据库在当时是一件非常重要的事情。将各种类型的值存储为字符串表示形式也是很常见的,如果不是这样,则只存储在当前机器上本机表示该值的字节。(XML、JSON,甚至是endianness交换对于当时的机器来说可能太贵了,或者至少对当时的想法来说是如此。)

扩展
dbm
来处理值的其他数据类型并不困难。它们不需要散列或比较,只是无损地存储和检索。由于
pickle
可以处理非常广泛的类型,效率也不算太低,而且是Python自带的,因此使用
pickle
是有意义的,所以
shelve
就是这样做的

但钥匙是另一回事。您需要的编码不仅是无损可逆的,而且还确保当且仅当两个值实际上相等时,两个值将编码为相等的字节。请记住,在Python中,
1==True
,但显然
pickle.dumps(1)!=pickle.dumps(True)
b'1'!=b'True'

如果您只关心字节类型,那么有很多类型可以无损地和保持相等地转换为字节。例如,对于Unicode字符串,只需使用UTF-8。(实际上,
shelve
会为您处理这个问题。)对于32位有符号整数,请使用
struct.pack('>I')
。对于三个字符串的元组,编码为UTF-8,反斜杠转义,并用换行符连接它们。等等对于许多特定领域,有一个简单的答案;没有一个通用的答案适用于大多数领域

因此,如果您想使用一个
dbm
来使用三个UTF-8字符串的元组作为键,您可以围绕
dbm
(或
shelve
)编写自己的包装器。与stdlib中的许多模块一样,
shelve
是一个有用的示例代码,也是一个可用的功能,这就是为什么有一个指向的链接。这很简单,新手应该能够弄清楚如何分叉、子类化它,或者包装它来进行自己的自定义密钥编码。(请注意,如果包装
搁置
,则