繁体   English   中英

Python lambda 闭包范围

[英]Python lambda closure scoping

我正在尝试使用闭包来消除函数签名中的变量(该应用程序是编写连接 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 或其他)关闭名称,而不是值。 当您定义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的局部变量。 每次调用makeFuncmakeFunc创建此局部变量的一个新值,并且返回的 lambda 创建一个闭包,该闭包“保存”了对makeFunc调用有效的局部变量值。 因此,循环中创建的每个函数都有自己的“私有”变量,称为x (对于这个简单的情况,这也可以使用外部函数的 lambda 来完成 --- stuff = [(lambda n: lambda x: x+n)(n) for n in [1, 2, 3]] - -- 但这不太可读。)

请注意,您仍然必须将n作为参数传递,只是通过这种方式,您不会将它作为参数传递给最终进入stuff列表的同一个函数; 相反,您将它作为参数传递给一个辅助函数,该函数创建您想要放入stuff的函数。 使用这种双函数方法的优点是返回的函数是“干净的”并且没有额外的参数; 如果您正在包装接受大量参数的函数,这可能很有用,在这种情况下,记住n参数在列表中的位置可能会变得混乱。 缺点是,这样做,使函数的过程更加复杂,因为您需要另一个封闭函数。

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM