The pickle
reference states that the set of objects which can be pickled is rather limited. Indeed, I have a function which returns a dinamically-generated class, and I found I can't pickle instances of that class:
>>> import pickle
>>> def f():
... class A: pass
... return A
...
>>> LocalA = f()
>>> la = LocalA()
>>> with open('testing.pickle', 'wb') as f:
... pickle.dump(la, f, pickle.HIGHEST_PROTOCOL)
...
Traceback (most recent call last):
File "<stdin>", line 2, in <module>
AttributeError: Can't pickle local object 'f.<locals>.A'
Such objects are too complicated for pickle
. Ok. Now, what's magic is that, if I try to pickle a similar object, but of a derived class, it works!
>>> class DerivedA(LocalA): pass
...
>>> da = DerivedA()
>>> with open('testing.pickle', 'wb') as f:
... pickle.dump(da, f, pickle.HIGHEST_PROTOCOL)
...
>>>
What's happening here? If this is so easy, why doesn't pickle
use this workaround to implement a dump
method that allows "local objects" to be pickled?
I disagree, you can pickle both. You just need to use a better serializer, like
dill
.dill
(by default) pickles classes by saving the class definition instead of pickling by reference, so it won't fail your first case. You can even usedill
to get the source code, if you like.I think you did not read the reference you cite carefully. The reference also clearly states that only the following objects are pickleable:
Your example
does not define a class at the top level of a module, it defines a class within the scope of
f()
.pickle
works on global classes, not local classes. This automatically fails the pickleable test.DerivedA
is a global class, so all is well.As for why only top-level (global to you) classes and functions can't be pickled, the reference answers that question as well (bold mine):
So there you have it.
pickle
only serialises objects by name reference, not by the raw instructions contained within the object. This is becausepickle's
job is to serialise object hierarchy, and nothing else.DerivedA
instances are pickleable becauseDerivedA
is available through a global variable matching its fully-qualified name, which is howpickle
looks for classes when unpickling.The problem with trying to do something like this with local classes is that there's nothing identifying which
A
class an instance corresponds to. If you runf
twice, you get twoA
classes, and there's no way to tell which one should be the class of unpickledA
instances from another run of the program. If you don't runf
at all, you get noA
classes, and then what the heck do you do about the type of unpickled instances?You can only pickle instances of classes defined at module's top level.
However, you can pickle instances of locally-defined classes if you promote them to top level.
You must set the __ qualname__ class attribute of the local class. Then you must assign the class to a top-level variable of the same name.