我有一个大字典(28 MB)'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)]]}
答案 0 :(得分:9)
shelve
实际上是一个不错的选择。它就像一个字典,但它由一个BDB(或类似的)键值数据库文件支持,Python将处理所有的缓存等,所以它不需要加载整个把事情当作记忆。
以下是如何创建搁置文件。请注意,架子键必须是字符串。另请注意,我是在原地创建货架,而不是先创建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)
如果您的使用字典的代码不适合放入范围,请将with
语句替换为普通open
,并在您使用close
时明确pickle
。重做。
dict
可能不是一个好主意,因为你仍然需要把整个事情都读到内存中。与导入定义巨型文字的模块相比,它可能会使用更少的瞬态内存,也可能使用磁盘空间,但仍然存在内存哈希表,这可能仍然存在问题。但你可以随时测试它,看看它的工作情况。
以下是如何创建pickle文件。请注意,您可以使用(几乎)任何您想要的键作为键,而不仅仅是字符串。但是,您必须先构建整个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)
。
import cPickle
def loaddict():
with open('mydict.pickle', 'rb') as f:
return cPickle.load(f)
这会创建一个47MB的文件。
现在,要在您的主应用中使用它:
pickle
与anydbm
相同的基本问题是针对任何其他必须保存和加载的持久性格式 - 无论是您自己编写的自定义格式,还是JSON或YAML等标准格式。 (当然,如果你需要与其他程序的互操作性,特别是在其他语言中,可以采用类似JSON的方式。)你最好使用数据库;唯一的问题是,什么样的数据库。
dict
类型数据库的优点是您可以像使用open
一样使用它,而不必担心如何加载/保存/访问它({1}除外}和close
行)。 anydbm
的问题在于它只允许您将字符串映射到字符串。
shelve
模块有效地包装anydbm
,并对每个值进行酸洗。你的钥匙仍然必须是字符串,但你的价值几乎可以是任何东西。因此,只要您的密钥是字符串,并且您没有从值到外部对象的任何引用,它就是dict
的非常透明的替代品。
其他选项 - sqlite3
,各种现代nosql数据库等 - 要求您更改访问数据的方式,甚至是您组织数据的方式。 (A"列表清单"不清楚ER模型。)当然,从长远来看,这可能会带来更好的设计,所以如果你认为你真的应该使用关系模型,考虑一下这个想法。
根据评论,@ ikea希望我解释为什么存在对dbm
和shelve
的某些限制。
首先,dbm
可以追溯到70年代。一个可以简单有效地将8位字符串映射到字符串的数据库是一个非常大的交易。将所有类型的值存储为其字符串表示形式也是很常见的 - 或者,如果不是这样,那么只存储恰好代表当前机器上的值的字节。 (XML,JSON,甚至是字节顺序交换对于当时的机器来说可能过于昂贵,或者至少是当时的思考。)
扩展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
旨在提供有用的示例代码以及可用的功能,这就是为the docs提供指向the source的链接的原因。它很简单,新手应该能够弄清楚如何分叉,子类化或包装它来做自己的自定义密钥编码。 (请注意,如果您打包shelve
,则必须将自定义值编码为str
,以便将str
编码为bytes
;如果您将其编码,或者将它子类化并覆盖相关方法,您可以直接编码到bytes
- 例如,上面调用struct.pack
。这对于简单性/可读性和性能可能更好。)