Search code examples
pythonpython-2.7picklenamedtuple

How to pickle a namedtuple instance correctly


I'm learning how to use pickle. I've created a namedtuple object, appended it to a list, and tried to pickle that list. However, I get the following error:

pickle.PicklingError: Can't pickle <class '__main__.P'>: it's not found as __main__.P

I found that if I ran the code without wrapping it inside a function, it works perfectly. Is there an extra step required to pickle an object when wrapped inside a function?

Here is my code:

from collections import namedtuple
import pickle

def pickle_test():
    P = namedtuple("P", "one two three four")
    my_list = []
    abe = P("abraham", "lincoln", "vampire", "hunter")
    my_list.append(abe)
    with open('abe.pickle', 'wb') as f:
        pickle.dump(abe, f)
    
pickle_test()

Solution

  • Create the named tuple outside of the function:

    from collections import namedtuple
    import pickle
    
    P = namedtuple("P", "one two three four")
    
    def pickle_test():
        my_list = []
        abe = P("abraham", "lincoln", "vampire", "hunter")
        my_list.append(abe)
        with open('abe.pickle', 'wb') as f:
            pickle.dump(abe, f)
    
    pickle_test()
    

    Now pickle can find it; it is a module global now. When unpickling, all the pickle module has to do is locate __main__.P again. In your version, P is a local, to the pickle_test() function, and that is not introspectable or importable.

    Note that pickle stores just the module and the class name, as taken from the class's __name__ attribute. Make sure that the first argument of the namedtuple() call matches the global variable you are assigning to; P.__name__ must be "P"!

    It is important to remember that namedtuple() is a class factory; you give it parameters and it returns a class object for you to create instances from. pickle only stores the data contained in the instances, plus a string reference to the original class to reconstruct the instances again.