Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to unpickle an object whose class exists in a different namespace (python)?

Tags:

python

pickle

If I have a script that defines a class:

script = """

class myClass:
    def __init__(self):
        self.name = 'apple'
        self.color = 'green'

"""

and then exec this script in its own namespace dict:

NS = {}
exec script in NS

and then create an instance of the class and pickle it:

a = NS['myClass']()

import pickle

save = pickle.dumps(a)

Now if I try to unpickle it:

load = pickle.loads(save)

I get the error

AttributeError: 'module' object has no attribute 'myClass'

I gather that this doesn't work because python doesn't know where to find myClass in order to rebuild the object. But myClass does exist in the NS dict. Is there a way to tell pickle where to find the class for the object it is loading?

like image 276
user1804375 Avatar asked Jan 09 '13 15:01

user1804375


People also ask

What is Unpickle in Python?

“Pickling” is the process whereby a Python object hierarchy is converted into a byte stream, and “unpickling” is the inverse operation, whereby a byte stream (from a binary file or bytes-like object) is converted back into an object hierarchy.

How do you pickle and Unpickle a model in Python?

In a nutshell Using pickle , simply save your model on disc with dump() function and de-pickle it into your python code with load() function. Use open() function to create and/or read from a . pkl file and make sure you open the file in the binary format by wb for write and rb for read mode.

What is pickling in Python with example?

Pickle in Python is primarily used in serializing and deserializing a Python object structure. In other words, it's the process of converting a Python object into a byte stream to store it in a file/database, maintain program state across sessions, or transport data over the network.


2 Answers

I discovered a solution this. It seems the problem is executing code in a dict prevents python from figuring out where the class is defined. The solution is to create an empty module, execute the code in the module, and then add the module to sys.modules so python knows about it.

script = """
class myClass:
    def __init__(self):
        self.name = 'apple'
        self.color = 'green'
"""

import imp, sys

moduleName = 'custom'

module = imp.new_module(moduleName)

exec script in module.__dict__

sys.modules[moduleName] = module

Now it is possible to pickle and unpickle an instance of the class:

import pickle
a = module.myClass()
s = pickle.dumps(a)
b = pickle.loads(s)
like image 158
user1804375 Avatar answered Oct 07 '22 01:10

user1804375


You can actually go one step further, and have the object reconstruct itself into whatever type you want.

import pickle
import copy_reg

class myClass(object):
    def __init__(self):
        self.apple = 'banana'

class otherclass(object):
    def __init__(self):
        self.apple = 'existential woe'

def pickle_an_object(o):
    print "pickling %s" % str(o)
    return otherclass, (o.apple,)

copy_reg.pickle(myClass, pickle_an_object)

foo = myClass()

s = pickle.dumps(foo)

del myClass
del otherclass

class otherclass(object):
    def __init__(self, appletype):
        self.apple = 'not %s' % appletype

o2 = pickle.loads(s)

print o2.apple

The basic idea is that you pack your class into a "trojan horse" of sorts, where its reconstruction causes an instantiation of a different class from what it originally was.

It does not matter what the otherclass on the pickling side contains. All that matters is that it exist at the same module path as the "destination" class - pickle is just putting a string representation of the module name into the serialized stream.

So, to break down what's happening in the above code in detail:

  • We register a custom pickler for myClass. This can be done via copy_reg or the __reduce_ex__ function.
  • Our custom pickler says "pickle this as an instance of otherclass" (which is a dummy. You do not need the "real" contents of otherclass on the pickling side, because all that goes into the pickle is the module/class name).
  • We pickle the object and "send it across the wire", to where the real version of otherclass exists.
  • On the remote side, otherclass is instantiated with the data from the tuple returned by the custom pickling function.

Python can be pretty powerful!

like image 31
Borealid Avatar answered Oct 07 '22 02:10

Borealid