[Pyrex] pickling pyrex classes

William Stein wstein at gmail.com
Mon Mar 26 20:20:37 UTC 2007


On Monday 26 March 2007 12:43 pm, Robert Kern wrote:
> Brian Blais wrote:
> > Hello,
> >
> > I have a class defined in Pyrex (to get speed increases), and python
> > complains that is can't be pickled (class def included below).  I looked
> > the documentation, and see that I can define __setstate__ and
> > __getstate__ methods to pickle only certain methods/class variables. 
> > Unfortunately, I am not clear on which ones are causing the problem, so I
> > figured I'd ask on the list before doing trial-and-error.  I imagine it
> > might be the "double *" , and perhaps the cdef methods, but I am not
> > sure.
>
> There's no reliable way for the pickle machinery to introspect extension
> type instances like it does pure Python instances. Consequently, I believe
> that it simply throws up its hands unless if you have defined the
> appropriate protocol methods. No particular attribute is causing a problem,
> I don't think.

Here are some further remarks on how to define the appropriate protocol 
methods.

In the SAGE codebase (http://www.sagemath.org), which contains well over 
35,000 lines of Pyrex, we do a huge amount of pickling of Pyrex types.   Long 
ago, after many many hours of frustration, I learned that there are two key 
things to keep in mind when pickling types defined using Pyrex:

   (1) Use the cPickle module with protocol 2, e.g.,
                import cPickle
                cPickle.dumps(my_object, protocol=2)
       Using a different protocol, e.g., the default, is an exercise in 
       suffering and frustration.

   (2) Define the __reduce__ method (setstate and getstate are for pure 
       Python classes).  Here's a typical example (for gmp integers, by 
       the way):
    def __reduce__(self):
        # This is the *trick* needed to pickle pyrex extension types.
        # The trick is that you must put a pure Python function
        # as the first argument, and that function must return
        # the result of unpickling with the argument in the second
        # tuple as input. All kinds of problems happen
        # if we don't do this.
        return sage.rings.integer.make_integer, (self.str(32),)

In this case, make_integer is (you have to see the relevant files to know
what the Integer and PY_NEW things are, but they aren't really relevant):

        def make_integer(s):
           cdef Integer r
           r = PY_NEW(Integer)
           r._reduce_set(s)
           return r

 -- William



More information about the Pyrex mailing list