Python lambdaclosures范围

我试图使用闭包来消除函数签名中的variables(应用程序是编写连接接口的Qt信号所需的所有函数,以控制大量参数到存储值的字典)。

我不明白为什么使用不包含在另一个函数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 

原因是闭包(lambdas或其他)closures名称,而不是值。 当你定义lambda x: test_fun(n, x) ,n不会被计算,因为它在函数内部。 在函数被调用的时候被评估,在那个时候,这个值是循环的最后一个值。

你在开始时说,你想“使用闭包来消除函数签名中的variables”,但它并不是真的那样工作。 (请参阅下文,但是,根据“消除”的含义,可能会满足您的一种方式)。函数定义时,函数体内部的variables将不会被评估。 为了让函数获取variables的“快照”,因为它在函数定义时存在,您必须将该variables作为parameter passing。 通常的做法是给函数一个参数,其默认值是来自外部作用域的variables。 看看这两个例子的区别:

 >>> 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作为parameter passing给函数,将n的当前值“locking”到该函数中。 如果你想以这种方式locking值,你必须做这样的事情。 (如果它不这样工作,像全局variables这样的东西根本就不能工作;在使用时查找自由variables是非常重要的。)

请注意,这种行为没有任何特定的lambdaexpression式。 如果使用def来定义引用封闭范围中的variables的函数,那么同样的范围规则是有效的。

如果你真的想要的话,你可以避免将额外的参数添加到返回的函数中,但是为了做到这一点,你必须在另一个函数中包装该函数,如下所示:

 >>> 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不再是全局variables,而是封闭函数makeFunc的局部variables。 每次调用makeFuncmakeFunc创build一个新的局部variables值,返回的lambda会创build一个闭包,以“保存”调用makeFunc的局部variables值。 因此,循环中创build的每个函数都有自己的“私有”variablesx 。 (对于这个简单的例子,这也可以用外部函数的lambda来实现— stuff = [(lambda n: lambda x: x+n)(n) for n in [1, 2, 3]] – 但这是不太可读的。)

注意,你仍然必须把你的n作为parameter passing,只是通过这样做,你不会把它作为一个parameter passing给相同的函数, 而是将其作为parameter passing给一个帮助函数,该函数可以创build您想要放入函数的函数。 使用这种双函数方法的优点是返回的函数是“干净”的,没有额外的参数; 这可能是有用的,如果你包装的函数接受了大量的参数,在这种情况下,可能会变得混淆,记住n参数在列表中的位置。 缺点是,这样做,function的制作过程比较复杂,因为你需要另一个封闭的function。

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