msg200062 - (view) |
Author: Facundo Batista (facundobatista) * |
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) * |
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) * |
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) * |
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) * |
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) * |
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) * |
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) * |
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) * |
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) * |
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) * |
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) * |
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) |
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) * |
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 :)
|
|
Date |
User |
Action |
Args |
2022-04-11 14:57:52 | admin | set | github: 63471 |
2013-10-18 07:57:47 | ethan.furman | set | status: 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:13 | python-dev | set | nosy:
+ python-dev messages:
+ msg200192
|
2013-10-17 14:53:29 | ethan.furman | set | files:
+ issue19272.stoneleaf.01.patch keywords:
+ patch |
2013-10-17 02:04:03 | ethan.furman | set | messages:
+ msg200101 |
2013-10-16 23:35:20 | ethan.furman | set | messages:
+ msg200099 |
2013-10-16 22:00:39 | jcea | set | messages:
+ msg200090 |
2013-10-16 19:18:21 | r.david.murray | set | nosy:
+ r.david.murray messages:
+ msg200074
|
2013-10-16 18:59:12 | ethan.furman | set | messages:
+ msg200070 |
2013-10-16 18:58:14 | ethan.furman | set | messages:
+ msg200069 |
2013-10-16 18:29:01 | facundobatista | set | messages:
+ msg200068 |
2013-10-16 18:27:50 | facundobatista | set | messages:
+ msg200067 |
2013-10-16 17:50:45 | ethan.furman | set | nosy:
+ ethan.furman messages:
+ msg200066
|
2013-10-16 17:42:48 | jcea | set | messages:
+ msg200064 |
2013-10-16 17:38:58 | jcea | set | nosy:
+ jcea
|
2013-10-16 17:07:20 | amaury.forgeotdarc | set | nosy:
+ amaury.forgeotdarc messages:
+ msg200063
|
2013-10-16 16:56:36 | facundobatista | create | |