2012-11-13 43 views
24

我试图使用闭包,以消除从一个函数签名的变量(该应用程序是使写入所需的一个接口来控制的参数一个相当大的数量的字典连接Qt的信号的所有的功能即存储值)。Python的拉姆达闭合作用域

我不明白为什么使用lambda没有包裹在其他功能的情况下,对所有的情况下返回姓氏。

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 

回答

44

原因是闭包(lambda或其他)关闭名称而不是值。当你定义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与该函数的当前值的一个参数函数。如果你想以这种方式锁定值,你必须做这样的事情。 (如果它不以这种方式工作,像全局变量这样的东西根本不起作用;在使用时查找自由变量是至关重要的。)

请注意,此行为没有任何特定于lambda表达式。如果使用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) for n in [1, 2, 3]] ---但这是不太可读的。)

请注意,您仍然必须通过您的n作为参数,它是就是这样,通过这样做,你不会将它作为一个参数传递给相同的函数,最终进入stuff列表;而是将它作为参数传递给一个帮助函数,该函数将创建要放入stuff的函数。使用这种双函数方法的优点是返回的函数是“干净的”并且没有额外的参数;如果你打包接受很多参数的函数,这可能会很有用,在这种情况下,记住列表中的参数可能会变得混淆。缺点是,以这种方式进行,因为你需要另一个封闭的函数,所以制作函数的过程更加复杂。结果是存在一个折衷:你可以使函数创建过程更简单(即不需要两个嵌套函数),但是你必须使得结果函数更复杂一点(即它有这个额外的n=n参数)。或者你可以使函数更简单(即它没有参数n=),但是你必须使函数创建过程更加复杂(即,你需要两个嵌套函数来实现该机制)。

+0

这比对[这些更受欢迎的类似问题的回答]中编写的任何东西都更好地解释了这种Python行为(https://stackoverflow.com/questions/2295290/what-do-lambda-function-closures -capture/23557126) –