Python 从zipfile加载pickle文件

Python 从zipfile加载pickle文件,python,pickle,zipfile,Python,Pickle,Zipfile,由于某些原因,我无法使cPickle.load处理ZipFile.open()返回的文件类型对象。 如果我对ZipFile.open()返回的文件类型对象调用read(),我可以使用cPickle.loads 例如 import zipfile import cPickle # the data we want to store some_data = {1: 'one', 2: 'two', 3: 'three'} # # create a zipped pickle file # zf

由于某些原因,我无法使cPickle.load处理ZipFile.open()返回的文件类型对象。 如果我对ZipFile.open()返回的文件类型对象调用read(),我可以使用cPickle.loads

例如

import zipfile
import cPickle

# the data we want to store
some_data = {1: 'one', 2: 'two', 3: 'three'}

#
# create a zipped pickle file
#
zf = zipfile.ZipFile('zipped_pickle.zip', 'w', zipfile.ZIP_DEFLATED)
zf.writestr('data.pkl', cPickle.dumps(some_data))
zf.close()

#
# cPickle.loads works
#
zf = zipfile.ZipFile('zipped_pickle.zip', 'r')
sd1 = cPickle.loads(zf.open('data.pkl').read())
zf.close()

#
# cPickle.load doesn't work
#
zf = zipfile.ZipFile('zipped_pickle.zip', 'r')
sd2 = cPickle.load(zf.open('data.pkl'))
zf.close()

注意:我不想只压缩pickle文件,而是压缩许多其他类型的文件。这只是一个例子。

这是由于
zipfile
模块实现的伪文件对象存在缺陷(对于Python 2.6中引入的
zipfile
类的
.open
方法)。考虑:

>>> f = zf.open('data.pkl')
>>> f.read(1)
'('
>>> f.readline()
'dp1\n'
>>> f.read(1)
''
>>> 
.read(1)
--
.readline()
的顺序是
.loads
在内部执行的操作(在protocol-0 pickle上,是Python 2中的默认操作,在这里使用)。不幸的是,
zipfile
的缺陷意味着这个特定的序列不起作用,在第一个read/readline对之后产生一个虚假的“文件结尾”(.read返回一个空字符串)

不确定Python标准库中的这个bug是否在Python2.7中得到了修复——我要检查一下

编辑:刚刚检查过——该缺陷已在Python 2.7 rc1(当前最新的2.7版本的候选发行版)中修复。我还不知道它是否也在最新的bug修复版本2.6中得到了修复

再次编辑:Python2.6.5(Python2.6的最新bug修复版本)中仍然存在该bug——因此,如果您无法升级到2.7,并且需要从
ZipFile.open
中更好地处理伪文件对象,那么2.7修复的后端口似乎是唯一可行的解决方案

请注意,您不确定是否需要性能更好的伪文件对象;如果您可以控制转储调用并使用最新和最好的协议,那么一切都会很好:

>>> zf = zipfile.ZipFile('zipped_pickle.zip', 'w', zipfile.ZIP_DEFLATED)
>>> zf.writestr('data.pkl', cPickle.dumps(some_data, -1))
>>> sd2 = cPickle.load(zf.open('data.pkl'))
>>> 

加载中混合读取和读取行调用时,只有旧的粗糙向后兼容的“协议0”(默认值)需要正确的伪文件对象行为(协议0的速度也较慢,并且会导致较大的pickle,因此,除非与旧Python版本的向后兼容性,或者0产生的pickle的ascii特性是应用程序中的强制约束,否则绝对不建议使用协议0)。

您是否尝试过导入picklefork
?:p尝试告诉我们什么“不起作用”在这种情况下,意思是:我们没有水晶球。@John,复制并通过他给出的代码有多难?在最后一段代码中,它得到了一个
eoferor
。@Alex:一个人复制/粘贴回溯等的努力比多人复制/粘贴代码到猜测版本的PythonNo回溯要少得多(因为
cPickle
是C编码的,
.open
方法是在Python2.6中引入的——那么,猜测在哪里?(如果OP使用的是2.7——仍然只是一个发行候选版本——或者仍然罕见的Py3,我肯定希望得到提及或标记;-)。不是为发布不完整信息的一般想法辩护,而是这个问题(使用简短、完整、独立的代码)远远超出了平均水平,因此将其挑出来批评对我来说似乎很不合适——当然它可能会更好(提到2.6.5或任何特定版本,使用的操作系统,EOFError等等),但只是略为逊色。我在python 2.6.6上使用协议1和协议2得到了相同的虚假EOFError。