Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Python: pickling nested functions

Using the example

def foo(a):
    def bar(b):
        return a+b
    return bar

d = {1:foo(1), 2:foo(2)}

It appears that pickle module will not work with a function not defined at the module scope, so pickling 'd' will not work. Is there another pickling mechanism available that I should consider?

like image 276
Maxim Khesin Avatar asked Aug 18 '12 15:08

Maxim Khesin


People also ask

Can you pickle Python functions?

In Python, you can use pickle to serialize (deserialize) an object structure into (from) a byte stream. Here are best practices for secure Python pickling. Pickle in Python is primarily used in serializing and deserializing a Python object structure.

Can partial functions be pickled?

To summarize the answer: You can't pickle a partial object unless the function it wraps is globally accessible by under its __name__ (within its __module__ ). When you decorate that function, this requirement isn't met, since you're putting the decorator's wrapper in the place of the original function.

What Cannot be pickled in Python?

With pickle protocol v1, you cannot pickle open file objects, network connections, or database connections.


2 Answers

I'm afraid that you can't pickle nested functions.

The pickle module serializes functions by name. That is, if you have a function myfunc in a module mymodule it simply saves the name mymodule.myfunc and looks it up again when unserializing. (This is an important security and compatibility issue, as it guarantees that the unserializing code uses its own definition for the function, rather than the original definition which might be compromised or obsolete.)

Alas, pickle can't do that with nested functions, because there's no way to directly address them by name. Your bar function, for instance, can't be accessed from outside of foo.

If you need a serializable object that works like a function, you can instead make a class with a __call__ method:

class foo(object):
    def __init__(self, a):
        self.a = a
    def __call__(self, b): # the function formerly known as "bar"
        return self.a + b

This works just like the nested functions in the question, and should pose no problem to pickle. Do be aware though, that you'll need to have the same class definition available when you unserialize a foo instance.

like image 165
Blckknght Avatar answered Oct 14 '22 02:10

Blckknght


You can pickle nested functions if you use dill instead of pickle.

>>> import dill
>>>    
>>> def foo(a):
...   def bar(b):
...     return a+b
...   return bar
... 
>>> d = {1:foo(1), 2:foo(2)}
>>> 
>>> _d = dill.dumps(d)
>>> d_ = dill.loads(_d)
>>> d_
{1: <function bar at 0x108cfe848>, 2: <function bar at 0x108cfe8c0>}
>>> d[1](0) + d[2](10)
13
>>> 
like image 28
Mike McKerns Avatar answered Oct 14 '22 02:10

Mike McKerns