问题
I'm using type()
to dynamically generate classes that will ultimately be pickled. The problem is that the un-pickling process needs the definition of the class in order to re-construct the object that has been pickled.
This is where I'm stuck. I don't know how to somehow provide the unpickler a way to generate an instance from a class that was dynamically generated.
Any hints appreciated.
Thanks!
Here's an example of the problem:
>>> class Foo(object):
... pass
>>> g=type('Goo',(Foo,),{'run':lambda self,x: 2*x } )()
>>> cPickle.dumps(g)
PicklingError: Can't pickle <class '__main__.Goo'>: attribute lookup __main__.Goo failed
This evidently works, but only from dynamic classes created from a pickle-able base class (with find-able module definition):
import cPickle
class Foo(object): pass
def dynamic(): return type('Goo',(Foo,),{'run':lambda self,x: 2*x } )()
g=type('Goo',(Foo,),{'run':lambda self,x: 2*x , '__reduce__': lambda self: (dynamic,tuple()) } )()
gg=cPickle.loads ( cPickle.dumps(g) )
print gg.run(10)
回答1:
When the Pickler encounters an object of a type it knows nothing about, it looks for a reduce method. Defining this method when you build your custom class using type should solve the problem of pickling.
If you provide initial args then in addition you might need to define a getnewargs method
回答2:
You can assign a global name to your dynamically generated class to make it picklable.
>>> class Foo(object):
... pass
>>> class_name = 'Goo'
>>> my_class = type(class_name, (Foo, ), {'run': lambda self, x: 2*x })
>>> globals()[class_name] = my_class
>>> g = my_class()
>>> pickle.dumps(g)
Of course, you need to make sure that the names of your classes are unique.
回答3:
One idea would be to pickle a tuple with:
- The name of the dynamic class
- The subclass tuple (possibly in string form from repr())
- The class dictionary
- The actual instance
This would allow you to pickle a class and then reconstruct it later using type() and subclassing Unpickler.
回答4:
For non-dynamic classes, python's pickling mechanism records the module and class name as strings. At unpickle time, it automatically loads the class object from that module.
As mentioned in the original post, the problem here is that for dynamic classes, the class definition itself is not pickled by the default pickler. Since dynamic classes don't exist in a module's source file, unpickling a dynamic class generally won't work.
The trickiest part of pickling the class itself is storing the methods' bytecode. Buried in PiCloud, there's an enhanced pickler under the hood that can pickle dynamic functions you could probably use or extend it to handle your objects.
回答5:
I have never done it myself, but http://docs.python.org/library/pickle.html#subclassing-unpicklers seems to indicate that you can override the behavior by subclassing Unpickler
回答6:
Well, for the posterity; works with cloudpickle
import cloudpickle
class Foo(object):
pass
g=type('Goo',(Foo,),{'run':lambda self,x: 2*x } )()
cloudpickle.dumps(g)
来源:https://stackoverflow.com/questions/11658511/pickling-dynamically-generated-classes