我正在使用lambda函数进行tkinter的GUI编程.
最近我在实现打开文件的按钮时遇到困难:
self.file=""
button = Button(conf_f, text="Tools opt.",
command=lambda: tktb.helpers.openfile(self.file))
如您所见,我想定义一个可以更新的文件路径,并且在创建GUI时不知道.
我遇到的问题是我之前的代码是:
button = Button(conf_f, text="Tools opt.",
command=lambda f=self.file: tktb.helpers.openfile(f))
lambda函数有一个关键字参数来传递文件路径.在这种情况下,当self.file为时,参数f未更新.
我从代码片段中获取了关键字参数,并且我在任何地方都使用它.显然我不应该……
这对我来说仍然不清楚……有人可以解释一下这两种lambda形式之间的区别以及何时使用另一种形式?
谢谢!
解决方法:
我会尝试更深入地解释它.
如果你这样做
i = 0
f = lambda: i
你创建一个函数(lambda本质上是一个函数),它访问它的封闭范围的i变量.
在内部,它通过一个包含i的所谓闭包来实现.松散地说,它是一种指向真实变量的指针,它可以在不同的时间点保持不同的值.
def a():
# first, yield a function to access i
yield lambda: i
# now, set i to different values successively
for i in range(100): yield
g = a() # create generator
f = next(g) # get the function
f() # -> error as i is not set yet
next(g)
f() # -> 0
next(g)
f() # -> 1
# and so on
f.func_closure # -> an object stemming from the local scope of a()
f.func_closure[0].cell_contents # -> the current value of this variable
在这里,i的所有值 – 在它们的时间 – 存储在所述闭包中.如果函数f()需要它们.它从那里得到它们.
您可以在反汇编列表中看到差异:
这些函数a()和f()反汇编如下:
>>> dis.dis(a)
2 0 LOAD_CLOSURE 0 (i)
3 BUILD_TUPLE 1
6 LOAD_CONST 1 ( at 0xb72ea650, file "", line 2>)
9 MAKE_CLOSURE 0
12 YIELD_VALUE
13 POP_TOP
3 14 SETUP_LOOP 25 (to 42)
17 LOAD_GLOBAL 0 (range)
20 LOAD_CONST 2 (100)
23 CALL_FUNCTION 1
26 GET_ITER
>> 27 FOR_ITER 11 (to 41)
30 STORE_DEREF 0 (i)
33 LOAD_CONST 0 (None)
36 YIELD_VALUE
37 POP_TOP
38 JUMP_ABSOLUTE 27
>> 41 POP_BLOCK
>> 42 LOAD_CONST 0 (None)
45 RETURN_VALUE
>>> dis.dis(f)
2 0 LOAD_DEREF 0 (i)
3 RETURN_VALUE
将其与看起来像的函数b()进行比较
>>> def b():
... for i in range(100): yield
>>> dis.dis(b)
2 0 SETUP_LOOP 25 (to 28)
3 LOAD_GLOBAL 0 (range)
6 LOAD_CONST 1 (100)
9 CALL_FUNCTION 1
12 GET_ITER
>> 13 FOR_ITER 11 (to 27)
16 STORE_FAST 0 (i)
19 LOAD_CONST 0 (None)
22 YIELD_VALUE
23 POP_TOP
24 JUMP_ABSOLUTE 13
>> 27 POP_BLOCK
>> 28 LOAD_CONST 0 (None)
31 RETURN_VALUE
循环的主要区别是
>> 13 FOR_ITER 11 (to 27)
16 STORE_FAST 0 (i)
在b()与
>> 27 FOR_ITER 11 (to 41)
30 STORE_DEREF 0 (i)
在a()中:STORE_DEREF存储在单元格对象(闭包)中,而STORE_FAST使用“正常”变量,该变量(可能)的工作速度稍快一些.
lambda也有所不同:
>>> dis.dis(lambda: i)
1 0 LOAD_GLOBAL 0 (i)
3 RETURN_VALUE
这里有一个LOAD_GLOBAL,而上面的一个使用LOAD_DEREF.后者也是关闭的.
我完全忘记了lambda i = i:i.
如果您将值作为默认参数,它将通过完全不同的路径进入函数:i的当前值通过默认参数传递给刚刚创建的函数:
>>> i = 42
>>> f = lambda i=i: i
>>> dis.dis(f)
1 0 LOAD_FAST 0 (i)
3 RETURN_VALUE
这样函数被调用为f().它检测到缺少参数并使用默认值填充相应参数.所有这些都在调用函数之前发生;在函数中,您只需看到值并获取并返回.
还有另一种方法可以完成你的任务:只需使用lambda就好像它需要一个值:lambda i:i.如果你打电话给它,它会抱怨缺少一个参数.
但是你可以使用functools.partial来解决这个问题:
ff = [functools.partial(lambda i: i, x) for x in range(100)]
ff[12]()
ff[54]()
这个包装器获得一个可调用的和一些要传递的参数.生成的对象是一个可调用对象,它使用这些参数以及您为其提供的任何参数调用原始可调用对象.它可以在这里用来保持锁定到预期的值.
标签:python,lambda,tkinter
来源: https://codeday.me/bug/20190612/1228500.html