Jane Austine wrote:
class A:
def __init__(self,tick):
if tick:
self.foo=self.bar
else:
self.foo=self.bur
def bar(self):
print 'bar'
def bur(self):
print 'bur'
import pickle
pickle.dumps(A())
running this script results in "TypeError: can't pickle function objects"
Why does this happen? and what can I do?
By default pickle bypasses the __init__() method completely, it just saves
class name and the instance's __dict__. In your case __dict__ happens to
contain a bound method which cannot be pickled. I don't know why, maybe the
general case where the instance the method is bound to could be a different
object would get too messy.
The normal way to manipulate the state to be pickled/unpickled is to
implement the __getstate__()/__setstate__() pair, but in your case it is
easier to trigger invocation of __init__() by generating the appropriate
argument list via __getinitargs__().
Peter
<code>
import pickle
class A:
def __init__(self, tick):
if tick:
self.foo = self.bar
else:
self.foo = self.bur
def __getstate__(self):
# copy the __dict__ so that further changes
# do not affect the current instance
d = dict(self.__dict__)
# remove the closure that cannot be pickled
del d["foo"]
# return state to be pickled
return d
def __getinitargs__(self):
tick = self.foo == self.bar
# return argument tuple for __init__()
# all items must be pickleable
return (tick,)
def bar(self):
print "bar"
def bur(self):
print "bur"
if __name__ == "__main__":
# test it
for tick in [False, True]:
print "\ntick =", tick
a = A(tick)
a.foo()
s = pickle.dumps(a)
b = pickle.loads(s)
b.foo()
</code>