我想为属于我的扩展库的对象实现pickle支持.在启动时初始化了一个类Service的全局实例.所有这些对象都是由于某些Service方法调用而生成的,并且基本上属于它.服务知道如何将它们序列化为二进制缓冲区以及如何将缓冲区反序列化为对象.
似乎Pythons __ reduce__应该服务于我的目的 - 实施酸洗支持.我开始实现一个并意识到unpickler存在问题(第一个元素是一个元组,预期由__ reduce__返回).这个unpickle函数需要Service的实例才能将输入缓冲区转换为Object.这里有一些伪代码来说明问题:
class Service(object):
...
def pickleObject(self,obj):
# do serialization here and return buffer
...
def unpickleObject(self,buffer):
# do deserialization here and return new Object
...
class Object(object):
...
def __reduce__(self):
return self.service().unpickleObject, (self.service().pickleObject(self),)
Run Code Online (Sandbox Code Playgroud)
注意元组中的第一个元素.Python pickler不喜欢它:它说它是instancemethod并且它不能被pickle.显然,pickler试图将例程存储到输出中,并希望Service实例与函数名一起使用,但这不是我想要发生的.我不希望(并且实际上不能:服务不可选)将服务与所有对象一起存储.我希望在调用pickle.load之前创建服务实例,并且在unpickling期间以某种方式使用该实例.
这是我来自copy_reg模块的地方.它似乎应该解决我的问题.该模块允许动态地为每种类型注册pickler和unpickler例程,并且这些例程应该稍后用于此类型的对象.所以我将此注册添加到服务构造中:
class Service(object):
...
def __init__(self):
...
import copy_reg
copy_reg( mymodule.Object, self.pickleObject, self.unpickleObject )
Run Code Online (Sandbox Code Playgroud)
self.unpickleObject现在是一个绑定方法,将服务作为第一个参数,缓冲区作为第二个参数.self.pickleObject也是绑定方法,将服务和对象作为pickle.copy_reg要求pickleObject例程应遵循reducer语义并返回与之前类似的元组.这里又出现了问题:我应该作为第一个元组元素返回什么?
class Service(object):
...
def pickleObject(self,obj):
...
return self.unpickleObject, (self.serialize(obj),)
Run Code Online (Sandbox Code Playgroud)
在这种形式泡菜再次抱怨它不能pickle instancemethod.我试过没有 - 它也不喜欢它.我放了一些虚拟功能.这工作 - 意味着序列化阶段很好,但在unpickling期间它调用这个虚函数而不是我在服务构造函数中为mymodule.Object类型注册的unpickler.
所以现在我很茫然.很抱歉很长的解释:我不知道如何在几行中提出这个问题.我可以总结一下这样的问题:
首先,该模块在这里不太可能对您有太大帮助:它主要是一种向没有该方法的类copy_reg添加类似功能的方法,而不是提供任何特殊功能(例如,如果您想从某个库中pickle对象)__reduce__本身不支持它)。
返回的可调用对象__reduce__需要位于要取消pickle的对象的环境中,因此实例方法并不真正合适。正如Pickle 文档中提到的:
\n\n\n在 unpickle 环境中,此对象必须是一个类、注册为\n \xe2\x80\x9csafe 构造函数\xe2\x80\x9d 的可调用对象(见下文),或者它必须具有一个具有真值的属性\n
\n__safe_for_unpickling__。
因此,如果您定义了一个函数(而不是方法),如下所示:
\n\ndef _unpickle_service_object(buffer):\n # Grab the global service object, however that is accomplished\n service = get_global_service_object()\n return service.unpickleObject(buffer)\n\n_unpickle_service_object.__safe_for_unpickling__ = True\nRun Code Online (Sandbox Code Playgroud)\n\n_unpickle_service_object现在,您可以在方法的返回值中使用此函数__reduce__,以便您的对象Service在 unpickle 时链接到新环境的全局对象。