Vin*_*nod 3 python dictionary pickle
我有一个大字典(28 MB)'MyDict'存储在一个MyDict.py文件中.
如果我执行声明:
from MyDict import MyDict
Run Code Online (Sandbox Code Playgroud)
一个MemoryError异常被抛出.
如何使用cPickle或shelve模块访问此词典.
如何在访问或不访问MyDict时编写此MyDict.py文件.cPickleshelve
这个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)]]}
Run Code Online (Sandbox Code Playgroud)
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)
Run Code Online (Sandbox Code Playgroud)
并且要使用它,实际上不要读它; 将其保留为磁盘架:
from contextlib import closing
import shelve
with closing(shelve.open('mydict.shelf')) as d:
# Put all your actual work here.
print len(d)
Run Code Online (Sandbox Code Playgroud)
如果您的字典使用代码不适合放入范围,请将该with语句替换为plain open,并close在完成后显式替换.
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)
Run Code Online (Sandbox Code Playgroud)
这会创建一个47MB的文件.
现在,在主应用程序中使用它:
import cPickle
def loaddict():
with open('mydict.pickle', 'rb') as f:
return cPickle.load(f)
Run Code Online (Sandbox Code Playgroud)
与pickle任何其他必须保存和加载的持久性格式相同的基本问题- 无论是您自己编写的自定义格式,还是像JSON或YAML这样的标准.(当然,如果你需要与其他程序的互操作性,特别是在其他语言中,可以采用类似JSON的方式.)你最好使用数据库; 唯一的问题是,什么样的数据库.
anydbm类型数据库的优点是你可以像使用它一样使用它dict,而不必担心如何加载/保存/访问它(除了open和close行).问题anydbm是它只允许您将字符串映射到字符串.
该shelve模块有效地包装anydbm,每个值的酸洗.你的钥匙仍然必须是字符串,但你的价值几乎可以是任何东西.因此,只要您的键是字符串,并且您没有从值到外部对象的任何引用,它就是一个非常透明的替代品dict.
其他选项sqlite3- 各种现代的nosql数据库等 - 要求您改变访问数据的方式,甚至改变组织方式.("列表列表"不是一个明确的ER模型.)当然,从长远来看,这可能会导致更好的设计,所以如果你认为你真的应该使用关系模型,请考虑这个想法.
从评论中,@ ikta希望我解释为什么有些限制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,反斜杠转义,并使用换行符连接它们.等等.对于许多特定领域,有一个简单的答案; 没有针对大多数域名的通用答案.
因此,如果您想使用a dbm来表示三个UTF-8字符串的元组作为键,您可以编写自己的包装器dbm(或shelve).与stdlib中的许多模块一样,shelve它旨在提供有用的示例代码以及可用的功能,这就是文档具有指向源的链接的原因.这很简单,新手应该能够弄清楚如何分叉,子类化或包装它来做自己的自定义密钥编码.(请注意,如果你包shelve,你将不得不编码您的自定义值str只所以它可以编码str来bytes;如果你叉它,还是它的子类,并覆盖相关的方法,你可以不是直接编码为bytes-例如,该struct.pack呼叫以上这对于简单/可读性和性能来说可能更好.)