【问题标题】:load a pickle file from a zipfile从 zipfile 加载 pickle 文件
【发布时间】:2011-03-01 16:58:36
【问题描述】:

由于某种原因,我无法让 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 文件,而是压缩许多其他类型的文件。这只是一个例子。

【问题讨论】:

  • 你试过import picklefork吗? :P
  • 试着告诉我们在这种情况下“不起作用”是什么意思;我们没有水晶球。
  • @John,复制并超越他给出的代码有多难?它在最后一个 sn-p 中得到一个 EOFError
  • @Alex:一个人复制/粘贴回溯等的工作量远低于多个人将代码复制/粘贴到 Python 猜测版本的工作量
  • 这里没有回溯(因为cPickle 是C 编码的),并且.open 方法是在Python 2.6 中引入的——那么,猜测在哪里? (如果 OP 使用的是 2.7 - 仍然只是一个候选版本 - 或者仍然罕见的 Py3,我 would 肯定希望提及或标记;-)。不捍卫发布不完整信息的一般想法,但这个问题(具有简短、完整、独立的代码)方式高于 SO 平均水平,因此单独批评它对我来说似乎很不合适 - - 肯定会更好(提及 2.6.5 或任何特定版本,使用的操作系统,EOFError,...),但只是勉强。

标签: python pickle zipfile


【解决方案1】:

这是由于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 内部所做的(在协议 0 泡菜上,Python 2 中的默认值,这是您在此处使用的)。不幸的是,zipfile 的缺陷意味着这个特定的序列不起作用,在第一个读取/读取行对之后产生虚假的“文件结尾”(.read 返回一个空字符串)。

不确定 Python 标准库中的这个错误是否已在 Python 2.7 中修复——我将检查一下。

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

再次编辑:该错误仍然存​​在于 Python 2.6.5 中,这是 Python 2.6 的最新错误修复版本 - 所以如果您无法升级到 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'))
>>> 

load 中混合读取和读取行调用时,只有旧的向后兼容的“协议 0”(默认)需要适当的伪文件对象行为(协议 0 也较慢,并导致更大的泡菜,所以它是绝对不推荐,除非向后兼容旧的 Python 版本,或者 0 产生的 pickle 的纯 ascii 特性是应用程序中的强制性约束)。

【讨论】:

  • 我在 python 2.6.6 上使用协议 1 和 2 得到相同的虚假 EOFError。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-12-28
  • 2020-05-15
  • 1970-01-01
相关资源
最近更新 更多