Python:带有和不带有关键字参数的lambda函数行为? [英] Python: lambda function behavior with and without keyword arguments?

查看:54
本文介绍了Python:带有和不带有关键字参数的lambda函数行为?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在使用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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆