lambda in list comprehension acting funny
Steven D'Aprano
steve+comp.lang.python at pearwood.info
Fri Jul 13 23:43:24 EDT 2012
On Fri, 13 Jul 2012 19:31:24 -0700, rusi wrote:
> Consider the following
>
> def foo(x):
> i = 100
> if x:
> j = [i for i in range(10)]
> return i
> else:
> return i
A simpler example:
def foo():
i = 100
j = [i for i in range(10)]
return i
In Python 3, foo() returns 100; in Python 2, it returns 9.
> In python 2 two different 'i's could be returned. In python3 only one i
> can be returned.
> One could call it dynamic binding.
One could also call it a tractor, but it isn't either of those things.
The difference is whether or not list comprehensions create their own
scope. In Python 2, they don't; in Python 3, they do. Personally I don't
have an opinion as to which is better, but in neither case does this have
any thing to do with lexical versus dynamic binding.
> Evidently Guido thinks it a bug which is why he changed it.
It was a case that either list comps be changed to *not* expose their
loop variable, or generator expressions be changed *to* expose their loop
variable. The Python 2 situation where list comps and gen expressions
have opposite behaviour was unfortunate.
> The leakage of i in the OPs question is the same kind of bug.
Not at all. The OP was *deliberately* creating a closure using i -- under
those circumstances, it would be a bug if i *didn't* leak.
The OP's gotcha was:
1) he expected the closure to use early binding, where i in each function
was bound to the value of i in the enclosing scope at the moment the
function was defined;
2) but Python actually uses late binding for closures, where the value of
i in each function is set to the value of i in the enclosing scope when
the function is called.
As far as I can tell, Python always uses late binding for scopes; the
only time it does early binding is for default values of function
parameters.
--
Steven
More information about the Python-list
mailing list