Python pickle 调用 cPickle?

Python pickle calls cPickle?

我是 Python 的新手。我正在将别人的代码从 Python 2.X 改编到 3.5。该代码通过 cPickle 加载文件。我将所有 "cPickle" 事件更改为 "pickle" 因为我知道 pickle 在 3.5 中取代了 cPickle。我收到此执行错误:

NameError: name 'cPickle' is not defined

相关代码:

import pickle
import gzip
...
def load_data():
    f = gzip.open('../data/mnist.pkl.gz', 'rb')
    training_data, validation_data, test_data = pickle.load(f, fix_imports=True)
    f.close()
    return (training_data, validation_data, test_data)

load_data()被另一个函数调用时,错误发生在pickle.load行。但是,a) cPicklecpickle 都不再出现在项目中任何地方的任何源文件中(全局搜索)和 b) 如果我 运行 load_data() 分别在 Python shell 中(但是,我确实遇到了另一个数据格式错误)。 pickle 是否在调用 cPickle,如果是,我该如何停止它?

Shell: Python 3.5.0 |蟒蛇 2.4.0 (x86_64)| (默认,2015 年 10 月 20 日,14:39:26) [GCC 4.2.1 (Apple Inc. build 5577)] 在达尔文

IDE:IntelliJ 15.0.1,Python 3.5.0,蟒蛇

不清楚如何进行。任何帮助表示赞赏。谢谢。

您尝试加载的 pickled 数据似乎是由 运行ning 在 Python 2.7 上的程序版本生成的。数据是包含对 cPickle.

的引用的内容

问题在于,作为一种序列化格式,Pickle 假定您的标准库(以及在较小程度上您的代码)不会在序列化和反序列化之间更改布局。它在 Python 2 和 3 之间做了很多次。当发生这种情况时,Pickle 没有迁移路径。

您是否有权访问生成 mnist.pkl.gz 的程序?如果是这样,将其移植到 Python 3 并重新 运行 它以重新生成文件的 Python 3 兼容版本。

如果没有,您将不得不编写一个 Python 2 程序来加载该文件并将其导出为可以从 Python 3 加载的格式(取决于您的形状)数据,JSON 和 CSV 是流行的选择),然后编写一个 Python 3 程序加载该格式,然后将其转储为 Python 3 pickle。然后您可以从您的原始程序加载该 Pickle 文件。

当然,您 真正 应该做的是在您能够从 Python 3 加载导出的格式时停止,并使用上述格式作为您实际的长期存储格式。

将 Pickle 用于 受信任的 程序之间的短期序列化以外的任何事情(加载 Pickle 相当于 运行 在您的 Python VM 中使用任意代码) 是你应该积极避免的事情,尤其是因为你发现自己所处的确切情况。

实际上,如果您从 python2.x 中 pickle 了对象,那么通常可以被 python3.x 读取。此外,如果您从 python3.x 中腌制了对象,它们通常可以被 python2.x 读取,但前提是它们在 protocol 设置为 2 或更小的情况下被转储。

Python 2.7.10 (default, Sep  2 2015, 17:36:25) 
[GCC 4.2.1 Compatible Apple LLVM 5.1 (clang-503.0.40)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> 
>>> x = [1,2,3,4,5]
>>> import math
>>> y = math.sin
>>>     
>>> import pickle 
>>> f = open('foo.pik', 'w') 
>>> pickle.dump(x, f)
>>> pickle.dump(y, f)
>>> f.close()
>>> 
dude@hilbert>$ python3.5
Python 3.5.0 (default, Sep 15 2015, 23:57:10) 
[GCC 4.2.1 Compatible Apple LLVM 5.1 (clang-503.0.40)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> import pickle
>>> with open('foo.pik', 'rb') as f:
...   x = pickle.load(f)
...   y = pickle.load(f)
... 
>>> x
[1, 2, 3, 4, 5]
>>> y
<built-in function sin>

此外,如果您要查找 cPickle,现在是 _pickle,而不是 pickle

>>> import _pickle
>>> _pickle
<module '_pickle' from '/opt/local/Library/Frameworks/Python.framework/Versions/3.5/lib/python3.5/lib-dynload/_pickle.cpython-35m-darwin.so'>
>>> 

您还询问了如何阻止 pickle 使用内置 (C++) 版本。您可以使用 _dump_load_Pickler class(如果您喜欢使用 class 对象)来执行此操作。使困惑?旧的 cPickle 现在是 _pickle,但是 dumploaddumpsloads 都指向 _pickle… 而_dump_load_dumps_loads 指向纯 python 版本。例如:

>>> import pickle
>>> # _dumps is a python function
>>> pickle._dumps
<function _dumps at 0x109c836a8>
>>> # dumps is a built-in (C++)
>>> pickle.dumps
<built-in function dumps>
>>> # the Pickler points to _pickle (C++)
>>> pickle.Pickler 
<class '_pickle.Pickler'>
>>> # the _Pickler points to pickle (pure python)
>>> pickle._Pickler
<class 'pickle._Pickler'>
>>> 

所以如果你不想使用内置版本,那么你可以使用pickle._loads之类的。

在 Anaconda Python3.5 中: 可以访问 cPickle 作为

import _pickle as cPickle

感谢 Mike McKerns

这绕过了技术问题,但该文件的 py3 版本可能名为 mnist_py3k.pkl.gz 如果是这样,请尝试打开该文件。

github 中有一个代码可以做到这一点:https://gist.github.com/rebeccabilbro/2c7bb4d1acfbcdcf9156e7b9b7577cba

我试过了,很有效。您只需要指定编码,在本例中为 'latin1':

pickle.load(open('mnist.pkl','rb'), encoding = 'latin1')