Python标准库中的默认pickle
模块不允许使用__main__
中的闭包,lambdas或函数对函数进行序列化(请参阅here)。
我需要使用一些自定义函数来挑选一个对象,这些函数在它们将被打开的情况下是不可导入的。还有一些其他Python对象序列化程序,包括dill
和cloudpickle
,它们能够执行此操作。
cloudpickle
documentation似乎在说,即使您使用cloudpickle
进行挑剔,也可以使用标准pickle
模块进行补充。这非常有吸引力,因为我甚至无法在我需要解开的环境中安装软件包。
实际上,文档中的示例基本上执行以下操作:
>>> 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
,运行第一个块,然后尝试使用Python3加载第二个块的pickle文件(其中cloudpickle
不是安装)。
这里发生了什么?如果甚至没有调用标准cloudpickle
加载,为什么需要安装pickle
?
答案 0 :(得分:1)
理论上,cloudpickle
不需要安装到load
一个腌制对象。从理论上讲,cloudpickle
所做的也包括在该对象中取消对象的所有必要功能。但是,这是理论上的。
在方法注册表中(例如,使用copyreg
),序列化程序需要注册使串行器能够创建所需类型的新对象并使其保存状态的方法。对于不需要在加载时安装序列化程序,序列化程序需要在pickle对象本身中包含所有必需的反序列化方法(这是可能的,因为pickle是递归的)。
cloudpickle
假定已安装cloudpickle
,因此(为了使生成的pickled对象更小),不包括所有必需的方法。这与numpy
不同,作为反例,dumps
上的numpy.array
方法确实包含了pickle中的reconstruct
方法(您可以将其视为{{ 1}}出现在numpy.core.multiarray\n_reconstruct
)的任何泡菜中。