Python:带有和不带有关键字参数的lambda函数行为? [英] Python: lambda function behavior with and without keyword arguments?
问题描述
我正在使用lambda函数通过tkinter进行GUI编程.最近,我在实现打开文件的按钮时遇到了麻烦:
I am using lambda functions for GUI programming with tkinter. Recently I got stuck when implementing buttons that open files:
self.file=""
button = Button(conf_f, text="Tools opt.",
command=lambda: tktb.helpers.openfile(self.file))
如您所见,我想定义一个文件路径,该路径可以更新,并且在创建GUI时不知道.我遇到的问题是,我的早期代码是:
As you see, I want to define a file path that can be updated, and that is not known when creating the GUI. The issue I had is that earlier my code was :
button = Button(conf_f, text="Tools opt.",
command=lambda f=self.file: tktb.helpers.openfile(f))
lambda函数具有关键字参数来传递文件路径.在这种情况下,当 self.file
更新时,参数 f
不会更新.
The lambda function had a keyword argument to pass the file path. In this case, the parameter f
was not updated when self.file
was.
我从一个代码片段中获得了关键字参数,并且在各处使用它.显然我不应该...
I got the keyword argument from a code snippet and I use it everywhere. Obviously I shouldn't...
对我来说这还不是很清楚...有人可以解释一下这两种lambda形式之间的区别以及何时使用另一种形式吗?
This is still not clear to me... Could someone explain me the difference between the two lambda forms and when to use one an another?
谢谢!
PS:以下评论将我引向了解决方案,但我想再解释一下: lambda与tkinter奇怪地工作
PS: The following comment led me to the solution but I'd like a little more explanations: lambda working oddly with tkinter
推荐答案
我将尝试更深入地解释它.
I'll try to explain it more in depth.
如果愿意
i = 0
f = lambda: i
您创建一个函数(lambda本质上是一个函数),该函数访问其封闭范围的 i
变量.
you create a function (lambda is essentially a function) which accesses its enclosing scope's i
variable.
在内部,它通过具有一个包含 i
的所谓闭包来实现.简而言之,它是一种指向实变量的指针,该实变量可以在不同的时间点保存不同的值.
Internally, it does so by having a so-called closure which contains the i
. It is, loosely spoken, a kind of pointer to the real variable which can hold different values at different points of time.
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()
需要它们.它从那里得到他们.
Here, all values of i
are - at their time - stored in that said closure. If the function f()
needs them. it gets them from there.
您可以在反汇编清单上看到不同之处:
You can see that difference on the disassembly listings:
这些所说的函数 a()
和 f()
像这样反汇编:
These said functions a()
and f()
disassemble like this:
>>> dis.dis(a)
2 0 LOAD_CLOSURE 0 (i)
3 BUILD_TUPLE 1
6 LOAD_CONST 1 (<code object <lambda> at 0xb72ea650, file "<stdin>", 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
将其与看起来像
>>> 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
存储在 cell
对象(关闭)中,而 STORE_FAST
使用一个正常"变量,它(可能)会更快地工作.
in a()
: the STORE_DEREF
stores in a cell
object (closure), while STORE_FAST
uses a "normal" variable, which (probably) works a little bit faster.
lambda也有所作为:
The lambda as well makes a difference:
>>> dis.dis(lambda: i)
1 0 LOAD_GLOBAL 0 (i)
3 RETURN_VALUE
这里您有一个 LOAD_GLOBAL
,而上面的一个使用了 LOAD_DEREF
.后者也用于关闭.
Here you have a LOAD_GLOBAL
, while the one above uses LOAD_DEREF
. The latter, as well, is for the closure.
我完全忘记了 lambda i = i:i
.
如果将值作为默认参数,它将通过完全不同的路径进入函数: i
的当前值通过默认参数传递给刚创建的函数:
If you have the value as a default parameter, it finds its way into the function via a completely different path: the current value of i
gets passed to the just created function via a default parameter:
>>> i = 42
>>> f = lambda i=i: i
>>> dis.dis(f)
1 0 LOAD_FAST 0 (i)
3 RETURN_VALUE
通过这种方式,该函数被称为 f()
.它检测到缺少参数,并用默认值填充各个参数.所有这些都在调用函数之前发生.从该函数中,您仅看到该值已被获取并返回.
This way the function gets called as f()
. It detects that there is a missing argument and fills the respective parameter with the default value. All this happens before the function is called; from within the function you just see that the value is taken and returned.
还有另一种方法可以完成您的任务:只需使用lambda,就好像它需要一个值: lambda i:i
.如果您这样称呼,它会抱怨缺少参数.
And there is yet another way to accomplish your task: Just use the lambda as if it would take a value: lambda i: i
. If you call this, it complains about a missing argument.
但是您可以使用 functools.partial
来解决:
But you can cope with that with the use of functools.partial
:
ff = [functools.partial(lambda i: i, x) for x in range(100)]
ff[12]()
ff[54]()
此包装器获取一个可调用的对象以及要传递的许多参数.生成的对象是一个可调用对象,它使用这些参数以及您提供给它的所有参数来调用原始可调用对象.可以在此处用于将其锁定为预期的值.
This wrapper gets a callable and a number of arguments to be passed. The resulting object is a callable which calls the original callable with these arguments plus any arguments you give to it. It can be used here to keep locked to the value intended.
这篇关于Python:带有和不带有关键字参数的lambda函数行为?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!