可能的重复:
Python lambdas 和作用域
我希望以下内容会生成一个包含 3 个取值 0、1 和 2 的常量函数的列表:
lis = []
for i in range(3):
lis.append( lambda: i )
但它们最终都取值为 2。我希望深度复制能够解决问题,但它似乎不起作用。
可能的重复:
Python lambdas 和作用域
我希望以下内容会生成一个包含 3 个取值 0、1 和 2 的常量函数的列表:
lis = []
for i in range(3):
lis.append( lambda: i )
但它们最终都取值为 2。我希望深度复制能够解决问题,但它似乎不起作用。
@Boud 给出了一个很好的答案,解释了为什么你的代码不能像你期望的那样工作。i
基本上,您必须在 lambda 引用它之前评估它的值。这是一个有点hacky的方法:
lis = []
for i in range(3):
lis.append( lambda i=i: i )
这使用了 Python 的默认函数参数的值特性,例如在一个函数中可以这样写:
def f(i=10):
return i
现在,诀窍是参数的默认值存储在创建函数(方法,lambda 表达式)时。因此:
j = 10
def f(i=j):
return i
j = 20
print( f(125) ) # no matter that j has been changed, the output is...
>>> 125
同样的技巧也适用于 lambda。为了更清楚一点:
lis = []
for j in range(3):
lis.append( lambda i=j: i )
# Calling the lambdas
print(lis[1]())
>>> 1
你循环以避免写这样的东西:
lis.append( lambda: 0 )
lis.append( lambda: 1 )
lis.append( lambda: 2 )
您的意图是编写返回常量整数的 lambda 函数。但是您正在定义和附加一个返回 object 的函数i
。因此,3 个附加功能是相同的。
创建的函数背后的字节码返回i
:
In [22]: import dis
In [25]: dis.dis(lis[0])
3 0 LOAD_GLOBAL 0 (i)
3 RETURN_VALUE
In [26]: dis.dis(lis[1])
3 0 LOAD_GLOBAL 0 (i)
3 RETURN_VALUE
In [27]: dis.dis(lis[2])
3 0 LOAD_GLOBAL 0 (i)
3 RETURN_VALUE
调用这些函数中的任何一个都会返回示例代码中i
的最新值:2
In [28]: lis[0]()
Out[28]: 2
如果你删除i
对象,你会得到一个错误:
In [29]: del i
In [30]: lis[0]()
---------------------------------------------------------------------------
NameError Traceback (most recent call last)
<ipython-input-30-c9e334d64652> in <module>()
----> 1 lis[0]()
<ipython-input-18-15df6d11323a> in <lambda>()
1 lis = []
2 for i in range(3):
----> 3 lis.append( lambda: i )
NameError: global name 'i' is not defined
一个解决方案可能是继续使用循环来编写带有您需要的常量的代码并实际运行该代码:
In [31]: lis = []
...: for i in range(3):
...: exec 'lis.append( lambda: {} )'.format(i)
...:
结果如下:
In [44]: lis[0]()
Out[44]: 0
In [45]: lis[1]()
Out[45]: 1
In [46]: dis.dis(lis[0])
1 0 LOAD_CONST 1 (0)
3 RETURN_VALUE
In [47]: dis.dis(lis[1])
1 0 LOAD_CONST 1 (1)
3 RETURN_VALUE