Python lambda's binding to local values
Change x.append(lambda : pv(v))
to x.append(lambda v=v: pv(v))
.
You expect "python lambdas to bind to the reference a local variable is pointing to, behind the scene", but that is not how Python works. Python looks up the variable name at the time the function is called, not when it is created. Using a default argument works because default arguments are evaluated when the function is created, not when it is called.
This is not something special about lambdas. Consider:
x = "before foo defined"
def foo():
print x
x = "after foo was defined"
foo()
prints
after foo was defined
What do lambda function closures capture?
What do the closures capture exactly?
Closures in Python use lexical scoping: they remember the name and scope of the closed-over variable where it is created. However, they are still late binding: the name is looked up when the code in the closure is used, not when the closure is created. Since all the functions in your example are created in the same scope and use the same variable name, they always refer to the same variable.
There are at least two ways to get early binding instead:
The most concise, but not strictly equivalent way is the one recommended by Adrien Plisson. Create a lambda with an extra argument, and set the extra argument's default value to the object you want preserved.
More verbosely but also more robustly, we can create a new scope for each created lambda:
>>> adders = [0,1,2,3]
>>> for i in [0,1,2,3]:
... adders[i] = (lambda b: lambda a: b + a)(i)
...
>>> adders[1](3)
4
>>> adders[2](3)
5The scope here is created using a new function (another lambda, for brevity), which binds its argument, and passing the value you want to bind as the argument. In real code, though, you most likely will have an ordinary function instead of the lambda to create the new scope:
def createAdder(x):
return lambda y: y + x
adders = [createAdder(i) for i in range(4)]
Assign variable to local scope of function in Python
Variables undefined in the scope of a lambda
are resolved from the calling scope at the point where it's called.
A slightly simpler example...
>>> y = 1
>>> f = lambda x: x + y
>>> f(1)
2
>>> y = 2
>>> f(1)
3
...so you just need to set var
in the calling scope before calling your lambda
, although this is more commonly used in cases where y
is 'constant'.
A disassembly of that function reveals...
>>> import dis
>>> dis.dis(f)
1 0 LOAD_FAST 0 (x)
3 LOAD_GLOBAL 0 (y)
6 BINARY_ADD
7 RETURN_VALUE
If you want to bind y
to an object at the point of defining the lambda
(i.e. creating a closure), it's common to see this idiom...
>>> y = 1
>>> f = lambda x, y=y: x + y
>>> f(1)
2
>>> y = 2
>>> f(1)
2
...whereby changes to y
after defining the lambda
have no effect.
A disassembly of that function reveals...
>>> import dis
>>> dis.dis(f)
1 0 LOAD_FAST 0 (x)
3 LOAD_FAST 1 (y)
6 BINARY_ADD
7 RETURN_VALUE
When I change the variable name in a lambda function, the result changes
You're not saving the variable's value inside the lambda. You're saving a variable defined outside of the lambda. It's not necessarily a global variable, but to the scope of the lambda, it is declared outside of it. When the iteration terminates, the value of i
is 2. That's why when you iterate with for-loop using j
, the value inside the lambda is always 2
.
When you iterate on a for-loop using the i
variable, you're once again changing the state of i
before executing the lambda. That's why it gives you a different result.
To make the lambda get the variable's value only without keeping it dependant of any variable scope, do something like this:
a = []
for i in range(3):
a.append((lambda k: lambda x:(k+x))(i))
lambda k: ...
is used to pass the state of i
to the inner lambda. It's in fact executing the function, and returning lambda x: (k+x)
, where k
is a private variable from the lambda's scope.
Now when you try to print it using another variable, such as j
, or assigning a new value to i
or k
:
i = 256
k = 512
for j in range(3):
print(a[j](0))
The output is:
0
1
2
In Python, why do lambdas in list comprehensions overwrite themselves in retrospect?
The lambda didn't overwrite itself, it is i
that was overwritten. This is a common mistake regarding variable scopes. Try this:
[lambda a, i=i: i(a) for i in (f,g)][0](0)
(the difference is binding the value of i
at the time the lambda is created)
See also:
- Weird behavior: Lambda inside list comprehension
- What is “lambda binding” in Python?
- Python lambda's binding to local values
How does a lambda function refer to its parameters in python?
It looks a bit messy, but you can get what you want by doing something like this:
>>> fs = [(lambda y: lambda x: x + y)(i) for i in xrange(10)]
>>> [f(0) for f in fs]
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
Normally Python supports the "closure" concept similar to what you're used to in Javascript. However, for this particular case of a lambda expression inside a list comprehension, it seems as though i
is only bound once and takes on each value in succession, leaving each returned function to act as though i
is 9. The above hack explicitly passes each value of i
into a lambda that returns another lambda, using the captured value of y
.
a strange usage for lambda in python
It's being done to bind the value of worker
inside the lambda. Here's a simplified example of this technique:
>>> thunks = [lambda: i for i in range(5)]
>>> [thunk() for thunk in thunks]
[4, 4, 4, 4, 4]
>>>
>>> thunks = [lambda i=i: i for i in range(5)]
>>> [thunk() for thunk in thunks]
[0, 1, 2, 3, 4]
With the expression lambda: i
, i
is evaluated at the time the lambda is called, not when it is defined. Hence in the first example, the results from all of the thunks are 4
because that's the value that i
has at the end of the range(5)
loop.
With the expression lambda i=i: i
, now the value of i
is being evaluated immediately within the loop in order to provide the default value of the i
parameter. This allows each thunk to capture a unique value of i
.
The concept might be more clear if the parameter is given a different name instead of shadowing i
:
>>> thunks = [lambda n=i: n for i in range(5)]
>>> [thunk() for thunk in thunks]
[0, 1, 2, 3, 4]
In your example, the lambda could be written as:
worker_fn = lambda w=worker: w.run(sess, coord, FLAGS.t_max)
This behaves the same as the worker=worker: worker.run...
expression in your code, but might make it a little more clear that the purpose of the expression is to take the current value of worker
in the loop and pass it into the body of the lambda as a parameter.
Related Topics
Find Nearest Value in Numpy Array
CSS Problems with Flask Web App
How to Make My Python Module Available System Wide on Linux
Which Command to Use for Checking Whether Python Is 64Bit or 32Bit
Why Is Signal.Sigalrm Not Working in Python on Windows
Fake Serial Communication Under Linux
Opening Filenames with Colon (":") in Windows 7
Os.System() Execute Command Under Which Linux Shell
Virtualenv Uses Wrong Python, Even Though It Is First in $Path
Is There a Simple Way to Remove Multiple Spaces in a String
How to Use CSS Selectors to Retrieve Specific Links Lying in Some Class Using Beautifulsoup
High Kernel CPU When Running Multiple Python Programs
How Would a Python Script Running on Linux Call a Routine in a Python Script Running Under Wine