1

I am using python 3 with tkinter and I am having issues with a command I want to execute from a button. A variable number of buttons are generated, one for each visitor, and I am trying to call the function signOut from the button press whilst passing the relevent item (visitor) from the list to it.

I realise that the issue is with the for loop as by the time the button is pressed, i will == the last item in the list. How can I make it specific to the actual visitor. I can't seem to think of the solution. Any advice is appreciated.

buttonDictionary = {}
for i in range(0,len(currentVisitors)):
    buttonDictionary[i] = Button(bottomFrame, text=currentVisitors[i], command=lambda: signOut(topFrame, bottomFrame, currentVisitors[i]))
    buttonDictionary[i].pack()
Karl Knechtel
  • 62,466
  • 11
  • 102
  • 153

1 Answers1

2

It is my understanding that e.g. i in a lambda within a loop like this is referring to the variable i itself, not the value of the variable on each iteration, so that when the command callback is called, it will use the value of i at that moment, which as you noticed is the value on the last iteration.

One way to solve this is with a partial. partial will, in effect "freeze" the arguments at their current state in the loop and use those when calling the callback.

Try using a partial instead of a lambda like this:

from functools import partial

buttonDictionary = {}
for i in range(0,len(currentVisitors)):
    buttonDictionary[i] = Button(bottomFrame, text=currentVisitors[i], command=partial(signOut, topFrame, bottomFrame, currentVisitors[i]))
    buttonDictionary[i].pack()

Another way I have seen this done, but haven't tried, is to assign i to a new variable in your lambda each time:

command=lambda i=i: signOut(topFrame, bottomFrame, currentVisitors[i])

I have gotten burned bad more than once when I first started with Python by using lambdas in loops (including a case very similar to this trying to assign a callback to dynamically generated buttons in a loop). I even created a snippet that would expand to think_about_it('are you sure you want to use a lambda?') whenever I typed lambda just to remind me of the pain I caused myself with that...

elethan
  • 16,408
  • 8
  • 64
  • 87
  • That did indeed solve the problem. Thanks a lot for your reply! – user3931640 Aug 26 '16 at 21:18
  • @user3931640 Glad it worked! I have been burned by this a few times. The worst thing is when you don't notice it is happening. I will try to update my answer a little bit with an explanation as to *why* this happens, or at least link to someone who can explain it well. – elethan Aug 26 '16 at 21:21