Python:lambda函数行为有关键字参数和没有关键字参数?
我使用lambda函数进行tkinter的GUI编程。 最近,我在实现打开文件的按钮时遇到了问题:Python:lambda函数行为有关键字参数和没有关键字参数?,python,lambda,tkinter,Python,Lambda,Tkinter,我使用lambda函数进行tkinter的GUI编程。 最近,我在实现打开文件的按钮时遇到了问题: self.file="" button = Button(conf_f, text="Tools opt.", command=lambda: tktb.helpers.openfile(self.file)) 如您所见,我想定义一个可以更新的文件路径,而在创建GUI时,该路径是未知的。 我遇到的问题是,早些时候我的代码是: button = Button(conf_f, tex
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形式之间的区别以及何时使用它们吗
谢谢大家!
PS:下面的评论引导我找到了解决方案,但我希望得到更多的解释:
我将尝试更深入地解释它 如果你这样做
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 (<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
循环中的主要区别是
>> 13 FOR_ITER 11 (to 27)
16 STORE_FAST 0 (i)
在b()
vs
>> 27 FOR_ITER 11 (to 41)
30 STORE_DEREF 0 (i)
在a()
:STORE\u DEREF
存储在单元格中
对象(闭包),而STORE\u FAST
使用一个“normal”变量,该变量(可能)工作得快一点
lambda也起到了作用:
>>> dis.dis(lambda: i)
1 0 LOAD_GLOBAL 0 (i)
3 RETURN_VALUE
这里有一个LOAD\u GLOBAL
,而上面的一个使用LOAD\u 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来解决这个问题。部分:
ff = [functools.partial(lambda i: i, x) for x in range(100)]
ff[12]()
ff[54]()
这个包装器获取一个可调用的参数和一系列要传递的参数。结果对象是一个callable,它使用这些参数加上您给它的任何参数来调用原始callable。这里可以使用它来锁定预期值。您还需要什么解释?您链接到的问题的公认答案非常简洁地解释了差异。事实上,我想了解为什么参数值可以根据编码样式进行更新或不更新。因此,如果我想看到差异,我需要运行:1/dis.dis(lambda:I)
和2/dis.dis(lambda I=I:I)
。我将得到一个LOAD\u GLOBAL
和一个LOAD\u DEREF
(希望我是对的…)。我现在不能这样做,但我明天再试。非常感谢您的回答!我还需要进一步阅读函数闭包。“我从没听说过这件事……”普劳夫粗鲁地说,是的。我只是补充了一些想法。非常感谢你的回答。我不能说现在一切都清楚了,因为我不太习惯这种事情。但我想我理解了最重要的部分。我还注意到,正是tkinter绑定让我头脑混乱。由于需要将事件作为参数传递,因此需要lambda中的参数。之后,每次需要lambda函数时,我都使用kwd参数。这是不对的…@Plouff这不是一个容易的话题;最好记住,访问“最外层”变量意味着在执行时访问值,而不是在定义时。在我的例子中,我将尝试记住相反的语句:关键字参数的默认值存储在定义中,即使在lambda函数中也是如此。我以为存储的是指针而不是值。
ff = [functools.partial(lambda i: i, x) for x in range(100)]
ff[12]()
ff[54]()