Pickling dynamically generated classes?

Question:

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)
Asked By: reckoner

||

Answers:

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

Answered By: Andrew Cox

One idea would be to pickle a tuple with:

  1. The name of the dynamic class
  2. The subclass tuple (possibly in string form from repr())
  3. The class dictionary
  4. The actual instance

This would allow you to pickle a class and then reconstruct it later using type() and subclassing Unpickler.

Answered By: Alyssa Haroldsen

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

Answered By: Sharoon Thomas

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.

Answered By: Mr Fooz

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.

Answered By: Brecht Machiels

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)