Python 循环语句遇上匿函

当你想用循环语句在tkinter或者无论某个函参中写一个带参的函数,你可能会碰到这样的错误:

def e(n):
	print("E", n)
t = []
for i in range(10):
	t.append(lambda: e(i))
for p in t:
	p()

#Output
E 9
E 9
E 9
E 9
E 9
E 9
E 9
E 9
E 9
E 9Code language: PHP (php)

以上尽管直接看不常见,但是会出现在很多tkinter语句中,比如tkinter的按钮:

import tkinter as tk
root = tk.Tk()

def pr(xxx):
    print(xxx)

for i in range(10):
    g = tk.Button(root, text = str(i), 
                  command = lambda: pr(i)
                 )
    g.pack()
root.mainloop()Code language: JavaScript (javascript)

看到问题出现在哪了吗?

你想要给每个按钮触发事件一个ID,但是tkinter触发事件直接用 command = xxx 不能带参数,所以你选择了匿名函数。

但是这又出现了一个问题,正如开头所讲,这样的格式最后会导致 变量 i 变成循环最后一个数字。

匿名函数如同正常函数一样,不会在定义时就直接获取ID,而是调用后再获取,因此会变成图中的 9 (即 range(10)[-1]

如何解决?

不要想着将值赋给按钮对象,因为匿名函数获取的按钮对象也是最后一个按钮。

command = lambda n=i: pr(n)  #将原command参数这一行替换即可Code language: PHP (php)

函数参数的默认值会立即获取,所以n就是当前的i。

扩展一下,我在一本书上看过,似乎当一个函数的参数默认为列表时候:

def f(n, a=[]):
    a.append(n)
    print(a)Code language: PHP (php)

你执行第一次f(1),第二次f(2),第三次f(3),上面的 a 参数会依旧保存上一次执行时候的值。

听不明白?没关系:

发表回复