The pickle
module of python is a very handy module if you want to store and retrieve your python data structures to and from a file. Using that module you don't need to waste your time on writing your own export and import functions any more.
We will write a simple python module thing.py
with a very simple class Thing
, which will have save and load functionality to and from file. Beside the traditional import usage of this module, we also inted to use the module on its own as a standalone program, with the simple task of creating a Thing
and saving it to a file. The module file thing.py
:
import pickle
class Thing:
def __init__(self, s):
self.s = s
def __repr__(self):
return "Thing: %s" % self.s
def save(self, fileName):
"""Save thing to a file."""
f = file(fileName,"w")
pickle.dump(self,f)
f.close()
def load(fileName):
"""Return a thing loaded from a file."""
f = file(fileName,"r")
obj = pickle.load(f)
f.close()
return obj
# make load a static method
load = staticmethod(load)
if __name__ == "__main__":
# code for standalone use
foo = Thing("foo")
foo.save("foo.pickle")
After executing thing.py
as standalone script, we get a file foo.pickle
in the current directory.
We now write a script thingtest.py
to load the thing from this file after creating, saving and loading another thing object:
import thing
# create another thing and save it
bar = thing.Thing("bar")
bar.save("bar.pickle")
# load the two things from their files
baz = thing.Thing.load("bar.pickle")
foo = thing.Thing.load("foo.pickle")
# show the things
print foo
print bar
print baz
This script will fail with an error message like
File "./thingtest.py", line 11, in ?
foo = thing.Thing.load("foo.pickle")
...
AttributeError: 'module' object has no attribute 'Thing'
Apparently the loading of bar.pickle
succeeded, but the loading of foo.pickle
failed.
The reason for this is the following. Loading (unpickling) involves importing the module defining the object. The file foo.pickle
states that this module is __main__
which corresponds to thing
, because foo
was saved from the script defining the class Thing
. On the other hand, bar.pickle
states that the module is the imported module thing
which corresponds with thing.py
.
The unpickling of foo
in thingtest.py
fails because python finds no class Thing
in the module __main__
, which corresponds now with thingtest.py
and not with the wanted thing.py
.
Two possible solutions for this problem:
- Import
Thing
to the top level ofthingtest.py
by adding the import statementfrom thing import Thing
- Save the right module information in the pickle file when pickling from the standalone use of the script
thing.py
. This can be done with the following change to the code block for standalone use:if __name__ == "__main__": # code for standalone use t = Thing("foo") Thing.__module__ = "thing" t.save("foo.pickle")
The last solution seems better, because the right information is now in the pickle file and it does not force the loader of the pickle file to use a workaround to read it.