msg144819 - (view) |
Author: Tomáš Dvořák (Tomáš.Dvořák) |
Date: 2011-10-03 14:39 |
I have this python script, and run it in python 2.7.2 (installed from EPD_free 7.1-2 (32-bit), but I guess this has nothing to do with EPD. ----8<---fail.py------ class X(object): pass x = X() items = ["foo", "bar", "baz"] for each in items: setattr(x, each, lambda: each) print("foo", x.foo()) print("bar", x.bar()) print("baz", x.baz()) ----8<---fail.py------ I'd naively expect it to print ('foo', 'foo') ('bar', 'bar') ('baz', 'baz') ,but it surprisingly (and annoyingly) outputs ('foo', 'baz') ('bar', 'baz') ('baz', 'baz') Please, tell me that this is a bug :) I'd hate it if this was the intended behaviour. I spent two hours today before I found out this was the cause of my program to fail. Best regards, Tomáš Dvořák |
|
|
msg144820 - (view) |
Author: R. David Murray (r.david.murray) *  |
Date: 2011-10-03 14:45 |
Sorry. It is intended behavior. The lambda 'each' is bound to the local 'each', and by the time the lambda's execute, the value of 'each' is 'baz'. I'm going to turn this into a doc bug, because while I'm pretty sure this is documented *somewhere*, I don't see it in the programming FAQ, and it should be there. |
|
|
msg144821 - (view) |
Author: Ezio Melotti (ezio.melotti) *  |
Date: 2011-10-03 15:03 |
To understand better what's going on, try to change the value of 'each' after the 3 prints and then call again the 3 methods: you will see that they now return the new value of each. This is because the lambdas refer to global 'each' (that at the end of the loop is set to 'baz'). If you do setattr(x, each, lambda each=each: each), the each will be local to the lambda, and it will then work as expected. An entry in the FAQ would be useful, I thought it was there already but apparently it's not (I'm pretty sure I saw this already somewhere in the doc, but I can't seem to find where). |
|
|
msg144822 - (view) |
Author: Tomáš Dvořák (Tomáš.Dvořák) |
Date: 2011-10-03 15:13 |
Thank you all very much for the super-quick responses. I'm used to smalltalk, so the python variable binding behaviour is unnatural to me, but I guess there must have been some reasons for making it behave this way. Ezio, the "lambda each=each: each" trick works nicely, thanks a lot. But - what does it mean? :) I just don't know how to parse and understand it :-) Best regards, Tomáš Dvořák |
|
|
msg144823 - (view) |
Author: Ezio Melotti (ezio.melotti) *  |
Date: 2011-10-03 15:22 |
Maybe with a different name is less confusing: lambda return_value=each: return_value This copies the value of 'each' in a variable called 'return_value' that is local to the lambda. Since the copy happens when the lambdas are defined, 'return_value' has then the right value. |
|
|
msg178918 - (view) |
Author: Ezio Melotti (ezio.melotti) *  |
Date: 2013-01-03 07:05 |
I'm having some problem at deciding what the title of the FAQ should be, and what the actual problem is. ISTM that OP's problem is the same as: >>> x = 1 >>> def foo(): return x ... >>> x = 2 >>> foo() 2 except that he has 3 lambdas in a loop that get attached to an instance rather than a simple function -- but the problem is that in both cases the function references a global variable whose value is retrieved at calling time rather that being set at definition time. IOW the solution should be clear, but the code is complex enough that it's not easy to recognize the analogy with the simpler case. I'm not even sure this has anything to do with closures, unless you consider the global scope a closure. Maybe the "What are the rules for local and global variables in Python?" FAQ could be expanded with a few examples to cover this case too. |
|
|
msg178938 - (view) |
Author: R. David Murray (r.david.murray) *  |
Date: 2013-01-03 12:30 |
The FAQ (as in, this question gets asked again and again) is something like "why do the lambdas I define in a loop all return the same result when the input value was different when each one was defined?" The same applies to regular functions, but people almost never do that in a loop, so in that case they are more likely to think of the scoping issue. |
|
|
msg178960 - (view) |
Author: Ezio Melotti (ezio.melotti) *  |
Date: 2013-01-03 16:28 |
> "why do the lambdas I define in a loop all return the same result when > the input value was different when each one was defined?" I thought about that, but that sounds a bit too long/specific. It also has the problem that the issue is not strictly related to lambdas or loops (even if this combination might be more common), and doesn't say where the "result" come from. |
|
|
msg178968 - (view) |
Author: R. David Murray (r.david.murray) *  |
Date: 2013-01-03 17:23 |
The point is, it is a FAQ. We are talking about updating the FAQ document. It doesn't matter if the text is "too specific", if it is in fact a FAQ. And it is. |
|
|
msg178975 - (view) |
Author: Ezio Melotti (ezio.melotti) *  |
Date: 2013-01-03 19:22 |
Here's a patch. |
|
|
msg179064 - (view) |
Author: Ezio Melotti (ezio.melotti) *  |
Date: 2013-01-04 18:26 |
Attached a new patch. |
|
|
msg179071 - (view) |
Author: R. David Murray (r.david.murray) *  |
Date: 2013-01-04 19:19 |
Looks good to me. |
|
|
msg179091 - (view) |
Author: Roundup Robot (python-dev)  |
Date: 2013-01-04 22:51 |
New changeset fdc894d44d82 by Ezio Melotti in branch '2.7': #13094: add Programming FAQ entry about the behavior of closures. http://hg.python.org/cpython/rev/fdc894d44d82 New changeset 02933454b7ce by Ezio Melotti in branch '3.2': #13094: add Programming FAQ entry about the behavior of closures. http://hg.python.org/cpython/rev/02933454b7ce New changeset 827ddaaa45e4 by Ezio Melotti in branch '3.3': #13094: merge with 3.2. http://hg.python.org/cpython/rev/827ddaaa45e4 New changeset 1bf7ae6c5324 by Ezio Melotti in branch 'default': #13094: merge with 3.3. http://hg.python.org/cpython/rev/1bf7ae6c5324 |
|
|
msg179092 - (view) |
Author: Ezio Melotti (ezio.melotti) *  |
Date: 2013-01-04 22:52 |
Fixed, thanks for the review! |
|
|