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 of thingtest.py by adding the import statement from 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.