如何使我的SWIG扩展模块与Pickle一起使用?

时间:2012-02-16 10:57:19

标签: python swig pickle

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

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

3 个答案:

答案 0 :(得分:14)

好像我找到了适合我的simlple解决方案:

所以我们假设我们使用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()))

成功=)

答案 1 :(得分:5)

以下是一些其他方法。没有像accepted answer那样具有普遍适用性,但如果您的类符合某些(简单)要求,那么您可以通过使实例本身(非包装版本)可选择来使您的用户更容易进行酸洗。这些技术全部由LSST afw package使用。

请注意,使用__getstate__ / __setstate__对进行取消镜像时,__init__方法将被调用,这意味着除非您小心,你将拥有一个你无法做任何事情的对象(如果你继续获得NotImplementedError: Wrong number or type of arguments for overloaded function,这是一种可能性)。这促使我们使用__reduce__(或者您可以从__init__致电__setstate__)。

如果您是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的此代码段:

%{
    #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)

答案 2 :(得分:2)

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

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

也许这对某人有帮助。我希望这不是太微不足道。

我发布了它作为答案,因为我无法发表评论,并且知道这不是你编辑帖子的内容。