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

Plo*_*uff 4 python lambda tkinter

我正在使用lambda函数进行tkinter的GUI编程.最近我在实现打开文件的按钮时遇到困难:

self.file=""
button = Button(conf_f, text="Tools opt.",
        command=lambda: tktb.helpers.openfile(self.file))
Run Code Online (Sandbox Code Playgroud)

如您所见,我想定义一个可以更新的文件路径,并且在创建GUI时不知道.我遇到的问题是我之前的代码是:

button = Button(conf_f, text="Tools opt.",
        command=lambda f=self.file: tktb.helpers.openfile(f))
Run Code Online (Sandbox Code Playgroud)

lambda函数有一个关键字参数来传递文件路径.在这种情况下,参数f在何时没有更新self.file.

我从代码片段中获取了关键字参数,并且我在任何地方都使用它.显然我不应该......

这对我来说仍然不清楚......有人可以解释一下这两种lambda形式之间的区别以及何时使用另一种形式?

谢谢!

PS:以下评论让我得到了解决方案,但我想要一些解释: lambda与tkinter奇怪地合作

glg*_*lgl 8

我会尝试更深入地解释它.

如果你这样做

i = 0
f = lambda: i
Run Code Online (Sandbox Code Playgroud)

你创建一个函数(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
Run Code Online (Sandbox Code Playgroud)

在这里,所有的值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
Run Code Online (Sandbox Code Playgroud)

将其与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
Run Code Online (Sandbox Code Playgroud)

循环的主要区别是

        >>   13 FOR_ITER                11 (to 27)
             16 STORE_FAST               0 (i)
Run Code Online (Sandbox Code Playgroud)

b()

        >>   27 FOR_ITER                11 (to 41)
             30 STORE_DEREF              0 (i)
Run Code Online (Sandbox Code Playgroud)

in a():对象中的STORE_DEREF商店cell(闭包),同时STORE_FAST使用"正常"变量,它(可能)的工作速度稍快一些.

lambda也有所不同:

>>> dis.dis(lambda: i)
  1           0 LOAD_GLOBAL              0 (i)
              3 RETURN_VALUE
Run Code Online (Sandbox Code Playgroud)

在这里你有一个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
Run Code Online (Sandbox Code Playgroud)

这样函数被调用为f().它检测到缺少参数并使用默认值填充相应参数.所有这些都在调用函数之前发生; 在函数中,您只需看到值并获取并返回.

还有另一种方法可以完成你的任务:只需使用lambda,就像它需要一个值:lambda i: i.如果你打电话给它,它会抱怨缺少一个参数.

但你可以使用以下方法解决这个问题functools.partial:

ff = [functools.partial(lambda i: i, x) for x in range(100)]
ff[12]()
ff[54]()
Run Code Online (Sandbox Code Playgroud)

这个包装器获得一个可调用的和一些要传递的参数.生成的对象是一个可调用对象,它使用这些参数以及您为其提供的任何参数调用原始可调用对象.它可以在这里用来保持锁定到预期的值.