Python Cookbook-7.3 在 Pickling 的时候压缩
任务
你想以一种压缩的方式来 pickle 一般的 Python 对象。
解决方案
标准库模块 cPickle 和 gzip提供了所需的功能;你只需以适当的方式将它们粘合起来即可:
import cPickle,gzip
def save(filename,*objects):'''将对象存为压缩过的磁盘文件'''fil = gzip.open(filename,'wb')for obj in objects:cPickle.dump(obj,fil,proto=2)fil.close()
def load(filename):'''从压缩的磁盘文件中载入对象'''fil = gzip.open(filename,'rb')while True:try: yield cPickle.load(fil)except EOFError:breakfil.close()
讨论
一般来说,持久化和压缩能够很好地协作。cPickle使用协议2将Python对象存为一种紧凑的格式,但其生成的文件仍可以继续压缩。比如,在Linux box中,open(‘/usr/dict/share/words’).readlines()产生了超过45000个字符串的列表。用协议0处理此列表产生的磁盘文件是972KB,而用协议2则只需716KB。然而,如果同时使用gzip和协议 2,如解决方案所示,产生的文件只有 268KB,这样能节省很多磁盘空间。实际情况是,协议0产生的结果压缩效果最好,同时使用gzip和协议0能够节省更多的磁盘空间,产生的文件只有 252KB。不过,268和 252之间的差异没有太大的意义,协议2 有其他的优势,特别是在用于新风格类的实例时,所以我推荐解决方案中的函数混用方式。
不管你采用什么协议来储存数据,都不需要在重新载入数据的时候担心任何问题。协议本身是和数据一起存入文件的,所以cPickle.load 能够检测出它需要采用的协议。只要给它传递一个文件实例或者一个带有read方法的伪文件对象,cPickle.load 会一个接一个地依照顺序返回每个对象,并且在完成整个文件时抛出EOFError 异常。在本节方案中,我们用 cPickle.load 封装了一个生成器,这样你就可以用一个 for 声明简单地循环所有的对象,或者,根据你的需要,也可以使用类似于 list(load('somefile.gz’))这样的调用,返回一个包含所有被恢复的对象的列表。