Python:lambda函数行为有关键字参数和没有关键字参数?

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

我使用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形式之间的区别以及何时使用它们吗

谢谢大家!

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]()