This issue tracker has been migrated to GitHub, and is currently read-only.
For more information, see the GitHub FAQs in the Python's Developer Guide.

classification
Title: Can't pickle lambda (while named functions are ok)
Type: behavior Stage: resolved
Components: Library (Lib) Versions: Python 3.4
process
Status: closed Resolution: not a bug
Dependencies: Superseder:
Assigned To: docs@python Nosy List: amaury.forgeotdarc, docs@python, ethan.furman, facundobatista, jcea, python-dev, r.david.murray
Priority: normal Keywords: patch

Created on 2013-10-16 16:56 by facundobatista, last changed 2022-04-11 14:57 by admin. This issue is now closed.

Files
File name Uploaded Description Edit
issue19272.stoneleaf.01.patch ethan.furman, 2013-10-17 14:53 review
Messages (14)
msg200062 - (view) Author: Facundo Batista (facundobatista) * (Python committer) Date: 2013-10-16 16:56
This is ok:

Python 3.4.0a3+ (default:86af5991c809, Oct 13 2013, 16:42:52) 
...
>>> import pickle
>>> def f():  
...   pass
... 
>>> pickle.dumps(f)
b'\x80\x03c__main__\nf\nq\x00.'


However, when trying to pickle a lambda, it fails:
>>>         
>>> pickle.dumps(lambda: None)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
_pickle.PicklingError: Can't pickle <class 'function'>: attribute lookup builtins.function failed


(Found this because I'm getting the same problem but when trying to use concurrent.futures.ProcessExecutor)
msg200063 - (view) Author: Amaury Forgeot d'Arc (amaury.forgeotdarc) * (Python committer) Date: 2013-10-16 17:07
Functions are pickled by name, not by code.
Unpickling will only work if a function with the same name is present in in the same module (__main__ in your example)

This is why pickling a lambda won't work: they have no individual names.
msg200064 - (view) Author: Jesús Cea Avión (jcea) * (Python committer) Date: 2013-10-16 17:42
Would be interesting to be able to pickle "function.__code__".

Although, thinking about this, it would be not portable between Python releases, something that pickle guarantee.

Thoughs?
msg200066 - (view) Author: Ethan Furman (ethan.furman) * (Python committer) Date: 2013-10-16 17:50
According to the docs[1]:

12.1.4. What can be pickled and unpickled?

The following types can be pickled:

    - None, True, and False
    - integers, floating point numbers, complex numbers
    - strings, bytes, bytearrays
    - tuples, lists, sets, and dictionaries containing only picklable objects
    - functions defined at the top level of a module
    - built-in functions defined at the top level of a module
    - classes that are defined at the top level of a module
    - instances of such classes whose __dict__ or the result of calling
      __getstate__() is picklable 

Notice that lambda is not in that list.


The docs for concurrent.futures.ProcessPoolExecutor[2] state:

17.4.3. ProcessPoolExecutor

The ProcessPoolExecutor class is an Executor subclass that uses a pool of processes to execute calls asynchronously. ProcessPoolExecutor uses the multiprocessing module, which allows it to side-step the Global Interpreter Lock but also means that only picklable objects can be executed and returned.



[1] http://docs.python.org/3/library/pickle.html#what-can-be-pickled-and-unpickled
[2] http://docs.python.org/dev/libraryconcurrent.futures.html?highlight=concurrent#processpoolexecutor
msg200067 - (view) Author: Facundo Batista (facundobatista) * (Python committer) Date: 2013-10-16 18:27
Ethan, lambda functions are included in "functions defined at the top level of a module".

Probably we should note there something like "except lambdas, because function pickling is by name, not by code".
msg200068 - (view) Author: Facundo Batista (facundobatista) * (Python committer) Date: 2013-10-16 18:29
Jesús, Amaury:

What if pickle would assign a random unique name to the lambda (like, an UUID) so it can be pickled and unpickled?
msg200069 - (view) Author: Ethan Furman (ethan.furman) * (Python committer) Date: 2013-10-16 18:58
Yeah, that one line should say, "named functions defined at the top level of a module".

The following three paragraphs do, however, mention "named" several times.

Sounds like a doc issue.
msg200070 - (view) Author: Ethan Furman (ethan.furman) * (Python committer) Date: 2013-10-16 18:59
The problem with a randam unique name is making sure you get the same random unique name across different runs, different pythons, and different orders of execution.
msg200074 - (view) Author: R. David Murray (r.david.murray) * (Python committer) Date: 2013-10-16 19:18
So don't make it random, use a hash of the code object :)

(I'm not sure I'm serious, the thought just popped into my head...)
msg200090 - (view) Author: Jesús Cea Avión (jcea) * (Python committer) Date: 2013-10-16 22:00
Lambdas are "anonymous" functions, by definition. And, usually, they are not top level functions, but defined inside others.

If at your "top level" (module) you do:

"""
a = lambda x: 2*x
"""

You don't have an anonymous function, but a function called "a". You can argue why "def a() : return 2*x" can be picked, but "a = lambda x: 2*x" can not. They look similar.

What the poster actually wanted (tell me if I am wrong), I guess, is to be able to serialize the function code and send it to other process to be executed there. Something like "mobile" code. As is, pickle doesn't allow it (that is the reason I was brainstorming about being able to pickle "function.__code__" objects), and it is good because pickle guarantees compatibilities between Python versions, but "__code__" objects are particular to a certain Python virtual machine and certain version of it.

That said, projects like PYRO provide "mobile code". I think that a recipe for that would be nice in the documentation, with a big warning saying "be sure you have the same implementation in both sides", and explicit versioning in the client and version checking in the server, in the recipe (to make it clear that same version is something you need).
msg200099 - (view) Author: Ethan Furman (ethan.furman) * (Python committer) Date: 2013-10-16 23:35
From the pickle docs:

=====================================================================
Note that functions (built-in and user-defined) are pickled by “fully
qualified” name reference, not by value.  This means that only the
function name is pickled, along with the name of the module the
function is defined in. Neither the function’s code, nor any of its
function attributes are pickled. Thus the defining module must be
importable in the unpickling environment, and the module must contain
the named object, otherwise an exception will be raised. 

Similarly, classes are pickled by named reference, ...
=====================================================================

There is no bug here; there /may/ be a doc clarification here.

However, if we're talking about enhancing pickle we can take 3.3 off the table, and unless somebody gets busy quick 3.4 as well.

Personally, I don't see the need.  Just define your functions at the top level (with def, not lambda -- lambda functions are anonymous and don't have a useful __name__ attribute).
msg200101 - (view) Author: Ethan Furman (ethan.furman) * (Python committer) Date: 2013-10-17 02:04
Jesús Cea Avión added the comment:
> 
> If at your "top level" (module) you do:
> 
> """
> a = lambda x: 2*x
> """
> 
> You don't have an anonymous function, but a function called "a".

Actually, you do have an anonymous function, which happens to be bound to the name "a".

Compare:

    --> def a():
    ...     pass
    --> a.__name__
    'a'

and 

    --> a = lambda: None
    --> a.__name__
    '<lambda>'
msg200192 - (view) Author: Roundup Robot (python-dev) (Python triager) Date: 2013-10-18 07:46
New changeset d103ba56710e by Ethan Furman in branch 'default':
Issue #19272: slight clarification of pickle docs with regard to lambda.
http://hg.python.org/cpython/rev/d103ba56710e
msg200227 - (view) Author: Ethan Furman (ethan.furman) * (Python committer) Date: 2013-10-18 07:57
Added some clarification to the docs to make it clearer that lambda functions cannot be pickled.

Facundo [1], if you want to pursue being able to pickle lambda functions please open an enhancement issue.  Some of the questions that come to mind:

 1) for a random name, where will the name be stored  (I'm thinking a
    __pickle__ dict on the module)? 

 2) for pickling the code object itself, what are the ramifications
    (this is directly contrary to how pickle was designed) 

These questions should be discussed on PyDev, and will probably require a PEP.


[1] or anyone else :)
History
Date User Action Args
2022-04-11 14:57:52adminsetgithub: 63471
2013-10-18 07:57:47ethan.furmansetstatus: open -> closed

assignee: docs@python
versions: - Python 3.3
nosy: + docs@python

messages: + msg200227
resolution: not a bug
stage: resolved
2013-10-18 07:46:13python-devsetnosy: + python-dev
messages: + msg200192
2013-10-17 14:53:29ethan.furmansetfiles: + issue19272.stoneleaf.01.patch
keywords: + patch
2013-10-17 02:04:03ethan.furmansetmessages: + msg200101
2013-10-16 23:35:20ethan.furmansetmessages: + msg200099
2013-10-16 22:00:39jceasetmessages: + msg200090
2013-10-16 19:18:21r.david.murraysetnosy: + r.david.murray
messages: + msg200074
2013-10-16 18:59:12ethan.furmansetmessages: + msg200070
2013-10-16 18:58:14ethan.furmansetmessages: + msg200069
2013-10-16 18:29:01facundobatistasetmessages: + msg200068
2013-10-16 18:27:50facundobatistasetmessages: + msg200067
2013-10-16 17:50:45ethan.furmansetnosy: + ethan.furman
messages: + msg200066
2013-10-16 17:42:48jceasetmessages: + msg200064
2013-10-16 17:38:58jceasetnosy: + jcea
2013-10-16 17:07:20amaury.forgeotdarcsetnosy: + amaury.forgeotdarc
messages: + msg200063
2013-10-16 16:56:36facundobatistacreate