Как да накарам моя модул за разширение SWIG да работи с Pickle?

Имам модул за разширение за Python, който използва SWIG като обвивка и се опитвам да го сериализирам с Pickle и не успявам =)

  1. Ако някой има източник на разширение SWIG, което може да се кисели, ще се радва да го види!
  2. Изглежда, че трябва да внедря метода __reduce_ex__ в моя C++ код. Някой има ли пример за __reduce_ex__? Има подобен въпрос на Stackoverflow но пропуска manager_constructor спецификация и изпълнение.

person alexanderkuk    schedule 16.02.2012    source източник


Отговори (3)


Изглежда, че намерих просто решение, което работи за мен:

Да кажем, че имаме клас C, който е генериран с SWIG, след което го обвиваме с

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

успява =)

person alexanderkuk    schedule 17.02.2012
comment
В случай, че някой се интересува от паралелна оценка с mpi4py, трябваше допълнително да внедря предложеното решение тук, за да го накарате да работи с методи на екземпляр на (опакования) клас - person Fred Schoen; 02.09.2013
comment
@FredSchoen: сериализаторът dill може да се използва с mpi4py. Просто задайте сериализатора на dill вместо на pickle. Вижте: stackoverflow.com/questions/21779541/ - person Mike McKerns; 18.03.2015
comment
здрасти Опитвам се да направя това и получавам тази грешка в дефиницията на класа: клас PickalableBuStress(BuStress, PickalableSWIG): TypeError: метаклас конфликт: метакласът на производен клас трябва да бъде (нестрог) подклас на метакласовете на всички негови бази Можете ли помогне? - person dondublon; 24.06.2016

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

Обърнете внимание, че при премахване с помощта на двойката __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)
person Paul Price    schedule 05.11.2013
comment
Пакетът LSST afw оттогава премина към pybind11, който препоръчвам през SWIG тези дни. - person Paul Price; 01.05.2019

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

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

Може би това е полезно за някого. Надявам се, че не е твърде тривиално.

Публикувах го като отговор, тъй като не мога да коментирам и научих, че не е нещо, за което редактирате публикация.

person JeroenDM    schedule 12.04.2018