在没有安装 cloudpickle 的情况下解开 cloudpickle

Unpickle a cloudpickle without cloudpickle installation

Python 标准库中的默认 pickle 模块不允许使用闭包、lambda 或 __main__ 中的函数序列化函数(参见 here ).

我需要使用一些自定义函数来 pickle 一个对象,这些函数在将被取消 pickle 的地方将无法导入。还有一些其他 Python 对象序列化程序,包括 dillcloudpickle,它们能够执行此操作。

cloudpickle documentation 似乎在说,即使你使用 cloudpickle 进行 pickle,你也可以使用标准 pickle 模块进行 unpickle。这非常有吸引力,因为我什至无法在需要解包的环境中安装包。

确实,文档中的示例基本上执行以下操作:

泡菜:

>>> import cloudpickle
>>> squared = lambda x: x ** 2
>>> pickled_lambda = cloudpickle.dump(squared, open('pickled_file', 'w'))

拆开:

>>> import pickle
>>> new_squared = pickle.load(open('pickled_file', 'rb'))
>>> new_squared(2)

但是,运行在未安装 cloudpickle 的环境中使用第二个块,即使它从未导入,也会产生错误:

"ImportError: No module named cloudpickle.cloudpickle"

可能最容易重现的例子是为 Python2 安装 cloudpickle,第一个块 运行,然后尝试用第二个块加载 pickled 文件使用 Python3(未安装 cloudpickle)。

这是怎么回事?为什么 cloudpickle 需要安装到 运行 标准 pickle 负载,如果它甚至没有被调用?

理论上,cloudpickle 不需要安装到 load 腌制对象。从理论上讲,cloudpickle 所做的也将包括所有必要的函数,以便在该对象中取消选中该对象。不过,那是理论上的。

在方法注册表中(例如使用 copyreg),序列化程序需要注册使序列化程序能够创建所需类型的新对象并将其注入已保存状态的方法。对于不需要在加载时安装的序列化程序,序列化程序需要在 pickle 对象本身中包含所有必需的反序列化方法(这是可能的,因为 pickle 是递归的)。

cloudpickle 假定安装了 cloudpickle,因此(为了使生成的腌制对象更小),不包括所有必需的方法。这与 numpy 不同,作为反例,numpy.array 上的 dumps 方法确实包含 pickle 中的 reconstruct 方法(您可以将其视为 numpy.core.multiarray\n_reconstruct 出现在 array) 的任何 pickle 中。