Python lambda封闭范围 [英] Python lambda closure scoping

查看:110
本文介绍了Python lambda封闭范围的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我试图使用闭包从函数签名中删除一个变量(应用程序是编写连接Qt信号为接口控制大量的参数到存储值的字典所需的所有函数) 。



我不明白为什么使用 lambda 不包含在另一个函数中的情况返回所有

  names = ['a','b','c'] 

test_fun(name,x):
print name,x

def gen_clousure(name):
return lambda x:test_fun(name,x)
$ b b funcs1 = [gen_clousure(n)for n in names]
funcs2 = [lambda x:test_fun(n,x)for n in names]

这是我想要的
在[88]:for f in funcs1:
....:f(1)
a 1
b 1
c 1

不明白为什么我得到这个
在[89]:for f in funcs2:
....:f(1)
c 1
c 1
c 1


解决方案

原因是闭包(lambdas或其他)名称,而不是值。当定义 lambda x:test_fun(n,x)时,n不会被求值,因为它在函数内部。



你说在这个函数被调用的时候,开始,你想使用闭包从一个函数签名中消除一个变量,但它不是真的这样工作。 (请参见下面的方法,可能满足你,取决于你的意思是消除)。当函数定义时,函数体内的变量不会被求值。为了使函数获取变量的快照,因为它在函数定义时间存在,你必须将变量作为参数传递。通常的做法是给函数一个参数,它的默认值是来自外部范围的变量。查看这两个示例之间的区别:

 >>> stuff = [lambda x:n + x for n in [1,2,3]] 
>>> for f in stuff:
... print f(1)
4
4
4
>>> stuff = [lambda x,n = n:n + x for n in [1,2,3]]
>>> for f in stuff:
... print f(1)
2
3
4

在第二个示例中,将 n 作为函数的参数锁定该函数的当前值n。如果你想以这种方式锁定值,你必须这样做。 (如果它没有以这种方式工作,像全局变量这样的东西根本不工作;在使用时自由变量是必须查找的)。



请注意,没有关于此行为是特定于lambdas。如果使用 def 定义引用来自封闭范围的变量的函数,则相同的范围规则生效。



如果你真的想,你可以避免添加额外的参数到你返回的函数,但这样做你必须包装该函数在另一个函数,如下:

 >>> def makeFunc(n):
... return lambda x:x + n
>>>> stuff = [makeFunc(n)for n in [1,2,3]]
>>> for f in stuff:
... print f(1)
2
3
4

这里,内部lambda在调用时仍然查找 n 的值。但是 n 它不再是全局变量,而是包含函数 makeFunc 内的局部变量。每次调用 makeFunc 时,将创建此局部变量的新值,并且返回的lambda创建一个闭包,保存对该调用有效的局部变量值的 makeFunc 。因此,在循环中创建的每个函数都有自己的私有变量 x 。 (对于这个简单的情况,这也可以使用外部函数的一个lambda来完成 - stuff = [(lambda n:lambda x:x + n)(n) 2,3]] ---但这不太可读。)



请注意,您仍然必须传递 n 作为参数,只是这样做,你不会将它作为参数传递给相同的函数,它卷入到 stuff list;而是将其作为参数传递给辅助函数,该辅助函数创建要放入 stuff 的函数。使用这种双函数方法的优点是返回的函数是干净的,没有额外的参数;这可能是有用的,如果你包装接受很多参数的函数,在这种情况下,可能会变得混乱,记住 n 参数在列表中。缺点是,这样做,做功能的过程更复杂,因为你需要另一个封闭的函数。



结果是有一个折衷:你可以使函数创建过程更简单(即,不需要两个嵌套函数),但是你必须使得到的函数有点复杂(即,它有这个额外的 n = n argument)。或者你可以使函数更简单(即它没有 n = n参数),但是你必须使函数创建过程更复杂(即,嵌套函数来实现该机制)。


I am trying to use closures to eliminate a variable from a function signature (the application is to make writing all the functions needed for connecting Qt signals for an interface to control a largish number of parameters to the dictionary that stores the values ).

I do not understand why the case of using the lambda not wrapped in another function returns the last name for all cases.

names = ['a','b','c']

def test_fun(name,x):
    print name,x

def gen_clousure(name):
    return lambda x: test_fun(name,x)

funcs1 = [gen_clousure(n) for n in names]
funcs2 = [lambda x: test_fun(n,x) for n in names]

# this is what I want
In [88]: for f in funcs1:
   ....:     f(1)
a 1
b 1
c 1

# I do not understand why I get this
In [89]: for f in funcs2:
   ....:     f(1)
c 1
c 1
c 1

解决方案

The reason is that closures (lambdas or otherwise) close over names, not values. When you define lambda x: test_fun(n, x), the n is not evaluated, because it is inside the function. It is evaluated when the function is called, at which time the value that is there is the last value from the loop.

You say at the beginning that you want to "use closures to eliminate a variable from a function signature", but it doesn't really work that way. (See below, though, for a way that may satisfy you, depending on what you mean by "eliminate".) Variables inside the function body will not be evaluated when the function is defined. In order to get the function to take a "snapshot" of the variable as it exists at function-definition time, you must pass the variable as an argument. The usual way to do this is to give the function an argument whose default value is the variable from the outer scope. Look at the difference between these two examples:

>>> stuff = [lambda x: n+x for n in [1, 2, 3]]
>>> for f in stuff:
...     print f(1)
4
4
4
>>> stuff = [lambda x, n=n: n+x for n in [1, 2, 3]]
>>> for f in stuff:
...     print f(1)
2
3
4

In the second example, passing n as an argument to the function "locks in" the current value of n to that function. You have to do something like this if you want to lock in the value in this way. (If it didn't work this way, things like global variables wouldn't work at all; it's essential that free variables be looked up at the time of use.)

Note that nothing about this behavior is specific to lambdas. The same scoping rules are in effect if you use def to define a function that references variables from the enclosing scope.

If you really want to, you can avoid adding the extra argument to your returned function, but to do so you must wrap that function in yet another function, like so:

>>> def makeFunc(n):
...     return lambda x: x+n
>>> stuff = [makeFunc(n) for n in [1, 2, 3]]
>>> for f in stuff:
...     print f(1)
2
3
4

Here, the inner lambda still looks up the value of n when it is called. But the n it refers to is no longer a global variable but a local variable inside the enclosing function makeFunc. A new value of this local variable is created every time makeFunc is called, and the returned lambda creates a closure that "saves" the local variable value that was in effect for that invocation of makeFunc. Thus each function created in the loop has its own "private" variable called x. (For this simple case, this can also be done using a lambda for the outer function --- stuff = [(lambda n: lambda x: x+n)(n) for n in [1, 2, 3]] --- but this is less readable.)

Notice that you still have to pass your n as an argument, it's just that, by doing it this way, you don't pass it as an argument to the same function that winds up going into the stuff list; instead you pass it as an argument to a helper function that creates the function you want to put into stuff. The advantage of using this two-function approach is that the returned function is "clean" and doesn't have the extra argument; this could be useful if you were wrapping functions that accepted a lot of arguments, in which case it could become confusing to remember where the n argument was in the list. The disadvantage is that, doing it this way, the process of making the functions is more complicated, since you need another enclosing function.

The upshot is that there is a tradeoff: you can make the function-creation process simpler (i.e., no need for two nested functions), but then you must make the resulting function a bit more complicated (i.e., it has this extra n=n argument). Or you can make the function simpler (i.e., it has no n=n argument), but then you must make the function-creation process more complicated (i.e., you need two nested functions to implement the mechanism).

这篇关于Python lambda封闭范围的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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