lambda in for loop only takes last value
Closures in Python capture variables, not values. For example consider:
def f():
x = 1
g = lambda : x
x = 2
return g()
What do you expect the result of calling f()
to be? The correct answer is 2, because the lambda f
captured the variable x
, not its value 1 at the time of creation.
Now if for example we write:
L = [(lambda : i) for i in range(10)]
we created a list of 10 different lambdas, but all of them captured the same variable i
, thus calling L[3]()
the result will be 9 because the value of variable i
at the end of the iteration was 9
(in Python a comprehension doesn't create a new binding for each iteration; it just keeps updating the same binding).
A "trick" that can be seen often in Python when capturing the value is the desired semantic is to use default arguments. In Python, differently from say C++, default value expressions are evaluated at function definition time (i.e. when the lambda is created) and not when the function is invoked. So in code like:
L = [(lambda j=i: j) for i in range(10)]
we're declaring a parameter j
and setting as default the current value of i
at the time the lambda was created. This means that when calling e.g. L[3]()
the result will be 3 this time because of the default value of the "hidden" parameter (calling L[3](42)
will return 42 of course).
More often you see the sightly more confusing form
lambda i=i: ...
where the "hidden" parameter has the same name as the variable of which we want to capture the value of.
Please read about minimal examples. Without reading your code, I believe you have run into a well known issue addressed in previous questions and answers that needs 2 lines to illustrate. Names in function bodies are evaluated when the function is executed.
funcs = [lambda: i for i in range(3)]
for f in funcs: print(f())
prints '2' 3 times because the 3 functions are identical and the 'i' in each is not evaluated until the call, when i == 2. However,
funcs = [lambda i=i:i for i in range(3)]
for f in funcs: print(f())
makes three different functions, each with a different captured value, so 0, 1, and 2 are printed. In your statement
__cMenu.add_command(label="{}".format(option),
command=lambda: self.filter_records(column, option))
add option=option
before :
to capture the different values of option
. You might want to rewrite as
lambda opt=option: self.filter_records(column, opt)
to differentiate the loop variable from the function parameter. If column
changed within the loop, it would need the same treatment.