2

I encounter a part of code as follows, where workers is a list of class object:

worker_threads = []
for worker in workers:
    worker_fn = lambda worker=worker: worker.run(sess, coord, FLAGS.t_max)
    t = threading.Thread(target=worker_fn)
    t.start()
    worker_threads.append(t)

Normally I expect the syntax for lambda is lambda x : func(x), but in here what is the worker=worker used for ?

exteral
  • 991
  • 2
  • 12
  • 33
  • 1
    Does it work or does it cause error? – Aven Desta Feb 18 '21 at 18:32
  • I believe that is working just like any other param would in a function, it is setting the for loop `worker` as the default value of the lambda expression – Matt Feb 18 '21 at 18:34
  • [variable assignment is not allowed inside lambda expression.](https://stackoverflow.com/questions/45337189/can-you-assign-variables-in-a-lambda) – Aven Desta Feb 18 '21 at 18:35
  • 2
    It's binding the value of `worker` within the lambda to its value as of when the lambda was executed. If you don't do that, the value of `worker` will change within all the lambdas as the `for` loop iterates. – Samwise Feb 18 '21 at 18:35
  • Lemme see if I can come up with a minimum example of this as an actual answer... – Samwise Feb 18 '21 at 18:36
  • It might be working, but in the context of Zen - it’s unPythonic. – S3DEV Feb 18 '21 at 18:39

3 Answers3

5

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.

Samwise
  • 68,105
  • 3
  • 30
  • 44
-1

Not an answer but this works:

t = list()
for i in [1,2,3,4]:
  func = lambda x=i: x*x
  t.append(func)

print(t[1]) 
# <function <lambda> at 0x7fdba6e6d790>
print(t[1]()) 
# 4

So you can bind your variables/parameters to a lambda function

It is explained here

Aven Desta
  • 2,114
  • 12
  • 27
-2

Think about it like this:

def f(x):
    print(x)

f('yes')

>>> 'yes'

x = 'no'
def f(x=x):
    print(x)

f()
>>> 'no'

f('yes')
>>> 'yes'

Or in lambda world:


f = lambda x:print(x)

f('yes')

>>> 'yes'

x = 'no'
f = lambda x=x: print(x)

f()
>>> 'no'

f('yes')
>>> 'yes'

So to make the answer WHY explicit for the commenters here, doing this lets each lambda be defined with a different default x value.

Matt
  • 1,196
  • 1
  • 9
  • 22