6

The pickle documentation states that "when class instances are pickled, their class’s data are not pickled along with them. Only the instance data are pickled." Can anyone provide a recipe for including class variables as well as instance variables when pickling and unpickling?

rmodrak
  • 65
  • 6

3 Answers3

8

Use dill instead of pickle, and code exactly how you probably have done already.

>>> class A(object):
...   y = 1
...   x = 0
...   def __call__(self, x):
...     self.x = x
...     return self.x + self.y
... 
>>> b = A()
>>> b.y = 4
>>> b(2)
6
>>> b.z = 5
>>> import dill
>>> _b = dill.dumps(b)
>>> b_ = dill.loads(_b)
>>> 
>>> b_.z
5
>>> b_.x
2
>>> b_.y
4
>>>
>>> A.y = 100
>>> c = A()
>>> _c = dill.dumps(c)
>>> c_ = dill.loads(_c)
>>> c_.y
100
Mike McKerns
  • 33,715
  • 8
  • 119
  • 139
  • This looks like an excellent solution, thank you! I'll upvote you as soon as I'm able. – rmodrak Oct 16 '14 at 22:00
  • I'm curious though, can anyone provide an alternative solution using only standard library functions? – rmodrak Oct 16 '14 at 22:01
  • Yes, `dill` just uses `copy_reg`, `save_reduce`, and `save_global` under the covers, so you could do the same… it'd just be more work on your part. – Mike McKerns Oct 16 '14 at 22:07
0

You can do this easily using the standard library functions by using __getstate__ and __setstate__:

class A(object):
  y = 1
  x = 0

  def __getstate__(self):
    ret = self.__dict__.copy()
    ret['cls_x'] = A.x
    ret['cls_y'] = A.y
    return ret

  def __setstate__(self, state):
    A.x = state.pop('cls_x')
    A.y = state.pop('cls_y')
    self.__dict__.update(state)
0

Here's a solution using only standard library modules. Simply execute the following code block, and from then on pickle behaves in the desired way. As Mike McKerns was saying, dill does something similar under the hood.

Based on relevant discussion found here.

import copy_reg


def _pickle_method(method):
    func_name = method.im_func.__name__
    obj = method.im_self
    cls = method.im_class
    return _unpickle_method, (func_name, obj, cls)


def _unpickle_method(func_name, obj, cls):
    for cls in cls.mro():
        try:
            func = cls.__dict__[func_name]
        except KeyError:
            pass
        else:
            break
    return func.__get__(obj, cls)


copy_reg.pickle(types.MethodType, _pickle_method, _unpickle_method)
rmodrak
  • 65
  • 6