【问题标题】:How to make my SWIG extension module work with Pickle?如何让我的 SWIG 扩展模块与 Pickle 一起使用?
【发布时间】:2012-03-07 18:41:18
【问题描述】:

我有一个 Python 扩展模块,它使用 SWIG 作为包装器,我尝试用 Pickle 对其进行序列化,但失败了 =)

  1. 如果有人有可以腌制的 SWIG 扩展源,很想看看!
  2. 看来我应该在我的 C++ 代码中实现__reduce_ex__ 方法。有人有__reduce_ex__ 的例子吗? There is similar Stackoverflow question 但它省略了 manager_constructor 规范和实现。

【问题讨论】:

    标签: python swig pickle


    【解决方案1】:

    我必须对已接受的答案进行细微更改,以使其适用于我的情况。由于我的类的初始化函数有一些输入参数,我不得不在C.__init(self) 函数中添加一个额外的参数*arg,如下所示:

        class PickalableC(C, PickalableSWIG):
            def __init__(self, *args):
                self.args = args
                C.__init__(self, *args)
    

    也许这对某人有帮助。我希望它不会太琐碎。

    我将其发布为答案,因为我无法发表评论并了解到这不是您编辑帖子的目的。

    【讨论】:

      【解决方案2】:

      这里有一些额外的方法。没有一个像accepted answer 那样具有普遍适用性,但是如果您的类满足一些(简单)要求,那么您可以通过使实例本身(未包装的版本)可腌制来使您的用户更容易腌制。这些技术都被LSST afw package使用了。

      请注意,当使用 __getstate__/__setstate__ 对取消腌制时,__init__ 方法将不会被调用,这意味着除非你小心,否则你将拥有一个对象你无能为力(如果你不断收到NotImplementedError: Wrong number or type of arguments for overloaded function,这是一种可能性)。这促使我们使用__reduce__(或者您可以从__setstate__ 调用__init__)。

      如果您正在 SWIG-ing 类 Foo 接受您可以从实例访问的构造函数参数(例如,通过访问器),请将以下内容添加到您的接口 (.i) 文件中:

      %extend Foo {
      %pythoncode {
          def __reduce__(self):
              # Requires matching constructor: __init__(foo, bar)
              args = self.getFoo(), self.getBar()
              return self.__class__, args
      }
      }
      

      如果您可以使用默认构造函数创建对象,然后对其进行操作以恢复其以前的状态,请使用以下内容:

      %extend Foo {
      %pythoncode {
          def __getstate__(self):
              args = self.getFoo(), self.getBar()
              return args
          def __setstate__(self, state):
              # Requires empty constructor: __init__()
              self.__init__()
              foo, bar = state
              self.setFoo(foo)
              self.setBar(bar)
      }
      }
      

      或者,如果您的类可以将二进制数据序列化到/从内存中(例如,您自己的磁盘格式的一些内存表示):

      %include "cdata.i"
      
      %extend Foo {
      %pythoncode {
          def __reduce__(self):
              s = Serializer()
              self.serialize(s)
              size = s.getLength()
              data = cdata(s.getData(), size)
              return unreduceFoo, (data, size)
      }
      }
      
      %pythoncode {
      def unreduceFoo(data, size):
          s = Serializer(size)
          memmove(s.getData(), data)
          return Foo(s)
      }
      

      最后,如果您使用的是boost::serialization,请使用Sogo Mineo 的这个sn-p:

      %{
          #include <boost/serialization/serialization.hpp>
          #include <boost/archive/binary_oarchive.hpp>
          #include <boost/archive/binary_iarchive.hpp>
          #include <sstream>
      %}
      %include "std_string.i"
      
      %define %boost_picklable(cls...)
          %extend cls {
              std::string __getstate__()
              {
                  std::stringstream ss;
                  boost::archive::binary_oarchive ar(ss);
                  ar << *($self);
                  return ss.str();
              }
      
              void __setstate_internal(std::string const& sState)
              {
                  std::stringstream ss(sState);
                  boost::archive::binary_iarchive ar(ss);
                  ar >> *($self);
              }
      
              %pythoncode %{
                  def __setstate__(self, sState):
                      self.__init__()
                      self.__setstate_internal(sState)
              %}
          }
      %enddef
      
      %boost_picklable(Foo)
      

      【讨论】:

      • LSST afw 包已经切换到 pybind11,这些天我在 SWIG 上推荐它。
      【解决方案3】:

      似乎我找到了适合我的简单解决方案:

      假设我们有使用 SWIG 生成的 C 类,然后我们用

      class PickalableC(C, PickalableSWIG):
      
          def __init__(self, *args):
              self.args = args
              C.__init__(self)
      

      PickalableSWIG 在哪里

      class PickalableSWIG:
      
          def __setstate__(self, state):
              self.__init__(*state['args'])
      
          def __getstate__(self):
              return {'args': self.args}
      

      然后

      pickle.loads(pickle.dumps(C()))
      

      失败,但是

      pickle.loads(pickle.dumps(PickalableC()))
      

      成功 =)

      【讨论】:

      • 如果有人对使用 mpi4py 进行并行评估感兴趣,我需要另外实现建议的解决方案 here 以使其与(包装的)类的实例方法一起使用
      • @FredSchoen:dill 序列化程序可以与mpi4py 一起使用。只需将序列化程序设置为dill 而不是pickle。见:stackoverflow.com/questions/21779541/…
      • 嗨。我尝试这样做并在类定义上出现此错误: class PickalableBuStress(BuStress, PickalableSWIG): TypeError: metaclass conflict: the metaclass of a derived class must be a (non-strict) subclass of the metaclasses of its bases 你能帮忙?
      猜你喜欢
      • 2011-02-25
      • 1970-01-01
      • 1970-01-01
      • 2019-03-15
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-10-16
      相关资源
      最近更新 更多