-1

I try this:

def prints(string):
print(string)

from tkinter import *
root = Tk()
menu = Menu(root)
filemenu = Menu(menu, tearoff=0)
List = Menu(filemenu, tearoff=0)

labels=[x for x in range(1,4)]

for i in range(len(labels)):
    List.add_command(label=labels[i], command=lambda: prints(labels[i]))
List.add_command(label="!", command=lambda: prints("!"))
List.add_command(label="@", command=lambda: prints("@"))
List.add_command(label="#", command=lambda: prints("#"))
filemenu.add_cascade(label="List", menu=List)
menu.add_cascade(label="File", menu=filemenu)

root.config(menu=menu)
root.mainloop()

and it show : image

when I clicked !,@,# it print !,@,#

but when I clicked 1,2,3 it print 3,3,3 not 1,2,3

why it occurs?

4Tel
  • 1

2 Answers2

1

The for loop does not create a separate scope, regarding variable definitions, so the i variable inside your lambda is not local to it, it's defined in an outer scope. This means that it lives beyond the end of the for loop, retaining the last value (3, in your example).

The lambda definition stored the fact that you mean to access i, it did not store the value of i at definition time. The actual access to i to retrieve its value takes place when the lambda is run, not when the lambda is defined, which is why you always see 3.

The usual trick is to capture the value, at definition time, by storing it in a variable that is local to the lambda, typically by adding an argument:

for i in range(len(labels)):
    List.add_command(label=labels[i], command=lambda j=i: prints(labels[j]))
joao
  • 2,220
  • 2
  • 11
  • 15
0

You have to use partial instead of lambda like this:

def prints(string):
    print(string)

# Import partial
from functools import partial
from tkinter import *
root = Tk()
menu = Menu(root)
filemenu = Menu(menu, tearoff=0)
List = Menu(filemenu, tearoff=0)

labels=[x for x in range(1,4)]

for i in range(len(labels)):
    # Create the command
    command = partial(prints, labels[i])
    List.add_command(label=labels[i], command=command)
List.add_command(label="!", command=lambda: prints("!"))
List.add_command(label="@", command=lambda: prints("@"))
List.add_command(label="#", command=lambda: prints("#"))
filemenu.add_cascade(label="List", menu=List)
menu.add_cascade(label="File", menu=filemenu)

root.config(menu=menu)
root.mainloop()

This is the documentation for functools.partial.

TheLizzard
  • 7,248
  • 2
  • 11
  • 31