当我运行以下脚本时,两个lambda都在同一个文件--junk.txt上运行os.startfile()。我希望每个lambda使用值“f”设置为lambda创建时。有没有办法让这个功能像我期望的那样发挥作用?
import os
def main():
files = [r'C:\_local\test.txt', r'C:\_local\junk.txt']
funcs = []
for f in files:
funcs.append(lambda: os.startfile(f))
print funcs
funcs[0]()
funcs[1]()
if __name__ == '__main__':
main()
答案 0 :(得分:22)
一种方法是:
def main():
files = [r'C:\_local\test.txt', r'C:\_local\junk.txt']
funcs = []
for f in files:
# create a new lambda and store the current `f` as default to `path`
funcs.append(lambda path=f: os.stat(path))
print funcs
# calling the lambda without a parameter uses the default value
funcs[0]()
funcs[1]()
否则在调用函数时会查找f
,因此您将获得当前(循环之后)值。
我更喜欢的方式:
def make_statfunc(f):
return lambda: os.stat(f)
for f in files:
# pass the current f to another function
funcs.append(make_statfunc(f))
或甚至(在python 2.5 +中):
from functools import partial
for f in files:
# create a partially applied function
funcs.append(partial(os.stat, f))
答案 1 :(得分:4)
重要的是要理解当变量成为闭包的一部分时,它本身就是变量,而不是包含的值。
这意味着在循环中创建的所有闭包都使用完全相同的变量f
,在循环结束时将包含循环内使用的最后一个值。
由于语言是如何定义的,但是这些捕获的变量在Python 2.x中是“只读”的:任何赋值都使变量成为局部变量,除非它被声明为global
(Python 3.x添加了{{1}允许写入外部作用域的本地的关键字。
正如Jochen Ritzel在他的回答中所说的那样,避免这种变量捕获并获得价值捕获的常用习惯是写
nonlocal
这是有效的,因为默认参数值是在函数创建时计算的,而lambda f=f: os.startfile(f)
不是外部变量,而是一个函数参数,它将你想要的值作为默认值(所以这个lambda只是一个默认的函数参数的值,不再关闭任何词法变量。)