What is the best way to write a __getstate__
method that pickles almost all of an object's attributes, but excludes a few?
I have an object with many properties, including one that references an instancemethod. instancemethod's are not pickleable, so I'm getting an error when I try to pickle this object:
class Foo(object):
def __init__(self):
self.a = 'spam'
self.b = 'eggs'
self.c = 42
self.fn = self.my_func
def my_func(self):
print 'My hovercraft is full of eels'
import pickle
pickle.dumps(Foo()) # throws a "can't pickle instancemethod objects" TypeError
This __getstate__
method fixes this, but then I have to manually include all the properties I want to serialize:
def __getstate__(self):
return { 'a': self.a, 'b': self.b, 'c': self.c }
That's not very scalable or maintainable if I have an object with many attributes or that changes frequently.
The only alternative I can think of is some kind of helper function that iterates through an object's properties and adds them (or not) to the dictionary, based on the type.
Yeah, I think that's pretty much what you're left with, if you want enough "magic" to allow yourself to be lazy (and/or allow for dynamically added attributes). Keep in mind that "
pickle
can't handle this" isn't the only reason you might not want to include something in the pickled state.But it's not as hard as you seem to think, assuming you have code for the "should I pickle this?" logic:
I'd cut to the root of your problem, and try to serialize the so-called 'un-pickleable' items first. To do this, I'd use dill, which can serialize almost anything in python. Dill also has some good tools for helping you understand what is causing your pickling to fail when your code fails.
If you absolutely wanted to, you could use dill's
badobjects
(or one of the other detection functions) to dive recursively into your object's reference chain, and pop out the unpickleable objects, instead of calling it at at every depth, as above.For the your specific case (preventing a function from getting pickled), use this:
self.__class__.fn = self.__class__.my_func
Now, instead of adding a function to an instance of a class, you've added it to the class itself, thus the function won't get pickled. This won't work if you want each instance to have its own version of
fn
.My scenario was that I wanted to selectively add
get_absolute_url
to some Django models, and I wanted to define this in an abstractBaseModel
class. I hadself.get_absolute_url = …
and ran into thepickle
issue. Just added__class__
to the assignment solved the issue in my case.Using
is_instance_method
from an earlier answer:Although the
is_instance_method
operation can also be performed less "magically" by taking an known instance method, saymy_func
, and taking its type.You could always just remove the bad items:
__slots__
solutionIf you are using slots, you can avoid repeating members to exclude with:
Tested in Python 2.7.6.