from tkinter import *
F=Tk()
i=1
while i<10:
newButton = Button(F,text="Show Number",command=lambda:showNumber(i))
newButton.pack(side=TOP)
i+=1
def showNumber(nb):
print(nb)
F.mainloop()
All buttons return 10. Why ?
I want button 1 return 1, button 2 return 2...
Thank you very much for helping me
Your anonymous
lambda
functions are can be though of as closures (as @abernert points out, they're not actually closures in Python's case) - they "close over" the variablei
, to reference it later. However, they don't look up the value at the time of definition, but rather at the time of calling, which is some time after the entirewhile
loop is over (at which point,i
is equal to 10).To fix this, you need to re-bind the value of
i
to a something else for the lambda to use. You can do this in many ways - here's one:This is explained in the Python FAQ: Why do lambdas defined in a loop with different values all return the same result?.
Quoting the FAQ answer:
In other words, your new functions aren't storing the value of
i
, they're storing the variablei
. And they're all storing the same variablei
, which has the value10
at the end of your loop. In fact, if you add ani = 'spam'
right beforeF.mainloop()
, you'll see that all the buttons now print out the stringspam
instead of a number.This is very useful when you're trying to create closures—functions that can affect their defining environment.* But when you're not trying to do so, that can get in the way.
The simplest way around this is to use a parameter with a default value. Default values don't hold variables; just values, which are evaluated at the time the function is defined. So:
* Note that in this case, there aren't actually any closures involved, because
i
is a global, rather than a local in the enclosing scope. But really, this is just because Python has special handling for globals and doesn't need a closure here; conceptually, if you think of there being one, you won't get into any trouble unless you start looking at the__closure__
or__code__
attributes.