Python Lambda'S Binding to Local Values

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:

  1. 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.

  2. 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)
    5

    The 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



Leave a reply



Submit