While trying to bind a button with a for loop, but the value is the last one in the for loop.
Code:
def add(self):
for i in range(10)
button = Button(text="hello world")
button.bind(on_release=(lambda dt: self.function(i)))
self.add_widget(button)
def function(value):
print(value)
When I click on any button, it prints out 9, however, when I press the first one, I want output to be 0, then 1, etc...
CodePudding user response:
There appears to be a bug in your code:
The local variable i
is bound and updated in each loop iteration. In this case, the bug happens because lambda dt: self.function(i)
, where i
references the value of local i
in each loop iteration. As this is defined in the scope of a loop, i
will eventually end up at the last value in the iteration over range(10)
, or 9
in this case.
For a simple fix, update your lambda
definition to pass in the local variable i
as a default parameter, thus copying the current value of i
over to the lambda
locals:
lambda dt, i=i: self.function(i)
Another Example
Here is yet another reproducible example where the output is similar to the example with the for
loop you had posted - note that using a list
comprehension as below is just a shorthand way of writing a for
loop.
>>> funcs = [lambda: i for i in range(5)]
>>> [f() for f in funcs]
[4, 4, 4, 4, 4]
Scoping the value of i
to the lambda locals, fixes the bug that we notice in the above output:
>>> funcs = [lambda _i=i: _i for i in range(5)]
>>> [f() for f in funcs]
[0, 1, 2, 3, 4]