Tkinter中Button迭代使用lambda绑定事件均触发最后函数问题
问题根因
这是Python闭包的晚绑定机制导致的:循环中定义的lambda表达式里引用的变量n不会在函数定义时就固定取值,而是会在函数实际被触发(也就是点击按钮)时,才去查找n的当前值。等整个循环执行完成后,n的最终值是9,因此所有按钮绑定的回调触发时,都会拿到这个最终值9,输出最后一个按钮的对应内容。
可行实现方案
方案1:给lambda加默认参数固定索引值
利用Python函数默认参数在定义时就完成求值的特性,把循环当前的n作为默认参数传入lambda,就能把每个按钮对应的索引值提前固定住,修改后的代码如下:
from tkinter import * window = Tk() r = 1 links = [] for n in range(10): btn = Button(window, text=f'button {n}', cursor='hand2') btn.grid(row=r, column=3, sticky=W) # 把当前n作为默认参数num传入,固定取值 btn.bind('<Button-1>', lambda e, num=n: print(f'This is the Button number {num}')) links.append(btn) r += 1 window.mainloop()
方案2:用functools.partial提前绑定参数
functools.partial可以提前给函数绑定固定参数,生成参数预设的新回调函数,不需要依赖lambda的默认参数写法,代码可读性更高:
from tkinter import * from functools import partial def handle_click(btn_idx, e): print(f'This is the Button number {btn_idx}') window = Tk() r = 1 links = [] for n in range(10): btn = Button(window, text=f'button {n}', cursor='hand2') btn.grid(row=r, column=3, sticky=W) # 提前把当前n绑定到回调的第一个参数 btn.bind('<Button-1>', partial(handle_click, n)) links.append(btn) r += 1 window.mainloop()
方案3:直接用Button自带的command参数(tkinter常规写法)
tkinter的Button组件原生支持command参数绑定点击回调,不需要额外绑定<Button-1>左键事件,写法更简洁,同样通过默认参数固定索引即可:
from tkinter import * window = Tk() r = 1 links = [] for n in range(10): # command处直接传固定了n的回调 btn = Button( window, text=f'button {n}', cursor='hand2', command=lambda num=n: print(f'This is the Button number {num}') ) btn.grid(row=r, column=3, sticky=W) links.append(btn) r += 1 window.mainloop()
注意:如果用
command绑定回调,函数默认不会接收事件对象参数,和bind传参的逻辑有区别,写的时候不要多传不需要的参数。
内容的提问来源于stack exchange,提问作者Felix
相关产品推荐
相关产品推荐

