Как заставить мой модуль расширения SWIG работать с Pickle? - PullRequest
13 голосов
/ 16 февраля 2012

У меня есть модуль расширения для Python, который использует SWIG в качестве оболочки, и я пытаюсь сериализовать его с помощью Pickle, и у меня не получается =)

  1. Если у кого-нибудь есть источник расширения SWIG, который можно мариновать, был бы рад его увидеть!
  2. Похоже, мне следует реализовать метод __reduce_ex__ в моем коде C ++. У кого-нибудь есть пример __reduce_ex__? Существует аналогичный вопрос Stackoverflow , но в нем отсутствуют manager_constructor спецификация и реализация.

Ответы [ 3 ]

14 голосов
/ 17 февраля 2012

Похоже, я нашел простое решение, которое работает для меня:

Итак, допустим, у нас есть класс C, который был сгенерирован с помощью SWIG, а затем мы обертываем его с

class PickalableC(C, PickalableSWIG):

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

, гдеPickalableSWIG is

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

успешно =)

5 голосов
/ 06 ноября 2013

Вот несколько дополнительных методов. Ни один из них не имеет такой же общей применимости, как принятый ответ , но если ваш класс удовлетворяет некоторым (простым) требованиям, вы можете облегчить процесс засолки для своих пользователей, сделав возможность выбора самих экземпляров (не упакованных версий). Все эти методы используются пакетом LSST afw .

Обратите внимание, что при снятии травления с использованием пары __getstate__ / __setstate__ метод __init__ будет вызываться , а не , что означает, что если вы не будете осторожны, у вас будет объект, который Вы ничего не можете сделать с (если вы продолжаете получать NotImplementedError: Wrong number or type of arguments for overloaded function, это возможно). Это заставляет нас использовать __reduce__ (или вы можете позвонить __init__ с __setstate__).

Если вы SWIG-класс 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 голосов
/ 12 апреля 2018

Мне пришлось внести небольшие изменения в принятый ответ, чтобы он работал для моего случая. Поскольку функция инициализации моего класса имеет несколько входных аргументов, мне пришлось добавить дополнительный аргумент *arg к функции C.__init(self), например:

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

Может быть, это кому-нибудь пригодится. Я надеюсь, что это не слишком тривиально.

Я опубликовал это как ответ, так как не могу комментировать и узнал, что это не то, для чего вы редактируете сообщение.

...