6

我想为属于我的扩展库的对象实现酸洗支持。有一个在启动时初始化的类 Service 的全局实例。所有这些对象都是由于某些 Service 方法调用而产生的,并且本质上属于它。服务知道如何将它们序列化为二进制缓冲区以及如何将缓冲区反序列化回对象。

看来 Pythons __reduce__ 应该满足我的目的 - 实现酸洗支持。我开始实现一个,并意识到 unpickler 存在问题(第一个元素 od 预计将由 __reduce__ 返回的元组)。这个 unpickle 函数需要一个 Service 实例才能将输入缓冲区转换为一个对象。这里有一些伪代码来说明这个问题:

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),)

注意元组中的第一个元素。Python pickler 不喜欢它:它说它是 instancemethod 并且不能被pickle。显然,pickler 试图将例程存储到输出中,并希望 Service 实例与函数名称一起,但这不是我想要发生的。我不想(而且真的不能:服务不可选择)将服务与所有对象一起存储。我希望在调用 pickle.load 之前创建服务实例,并且在 unpickle 期间以某种方式使用该实例。

在这里,我来到了 copy_reg 模块。它再次出现,因为它应该可以解决我的问题。这个模块允许动态注册每个类型的pickler和unpickler例程,这些应该在以后用于这种类型的对象。所以我将此注册添加到服务构造中:

class Service(object):
  ...
  def __init__(self):
      ...
      import copy_reg
      copy_reg( mymodule.Object, self.pickleObject, self.unpickleObject )

self.unpickleObject 现在是一个绑定方法,将服务作为第一个参数,缓冲区作为第二个参数。self.pickleObject 也是将服务和对象绑定到 pickle 的方法。copy_reg 要求 pickleObject 例程应该遵循 reducer 语义并返回与以前相似的元组。这里问题又出现了:我应该返回什么作为第一个元组元素?

class Service(object):
  ...
  def pickleObject(self,obj):
      ...
      return self.unpickleObject, (self.serialize(obj),)

在这种形式中,泡菜再次抱怨它不能泡菜实例方法。我试过无 - 它也不喜欢它。我放了一些虚拟功能。这很有效 - 意味着序列化阶段顺利进行,但在 unpickling 期间它调用这个虚拟函数而不是 unpickler 我在 Service 构造函数中为 mymodule.Object 类型注册。

所以现在我很茫然。对不起,解释太长了:我不知道如何用几行来问这个问题。我可以这样总结我的问题:

  1. 如果我希望独立注册一个,为什么 copy_reg 语义要求我从 pickleObject 返回 unpickler 例程?
  2. 是否有任何理由更喜欢 copy_reg.constructor 接口来注册 unpickler 例程?
  3. 如何制作泡菜以使用我注册的 unpickler 而不是流中的一个?
  4. 我应该返回什么作为元组中的第一个元素作为 pickleObject 结果值?有“正确”的价值吗?
  5. 我是否正确地处理了整个事情?有不同/更简单的解决方案吗?
4

1 回答 1

3

首先,该copy_reg模块在这里不太可能对您有太大帮助:它主要是一种向__reduce__没有该方法的类添加类似功能而不是提供任何特殊功能的方法(例如,如果您想从某个库中提取对象本身不支持它)。

由返回的可调用__reduce__对象需要在要取消提取对象的环境中可定位,因此实例方法并不合适。如Pickle 文档中所述:

在 unpickling 环境中,该对象必须是一个类,一个注册为“安全构造函数”(见下文)的可调用对象,或者它必须具有一个 __safe_for_unpickling__具有真值的属性。

所以如果你定义了一个函数(不是方法)如下:

def _unpickle_service_object(buffer):
    # Grab the global service object, however that is accomplished
    service = get_global_service_object()
    return service.unpickleObject(buffer)

_unpickle_service_object.__safe_for_unpickling__ = True

_unpickle_service_object您现在可以在方法的返回值中使用此函数,__reduce__以便您的Service对象在取消腌制时链接到新环境的全局对象。

于 2012-08-27T08:12:17.147 回答