我希望有一个lambdas列表,它可以作为某种繁重计算的缓存,并注意到这一点:
>>> [j() for j in [lambda:i for i in range(10)]]
[9, 9, 9, 9, 9, 9, 9, 9, 9, 9]
虽然
>>> list([lambda:i for i in range(10)])
[<function <lambda> at 0xb6f9d1ec>, <function <lambda> at 0xb6f9d22c>, <function <lambda> at 0xb6f9d26c>, <function <lambda> at 0xb6f9d2ac>, <function <lambda> at 0xb6f9d2ec>, <function <lambda> at 0xb6f9d32c>, <function <lambda> at 0xb6f9d36c>, <function <lambda> at 0xb6f9d3ac>, <function <lambda> at 0xb6f9d3ec>, <function <lambda> at 0xb6f9d42c>]
这意味着lambda是唯一的函数,但它们在某种程度上都共享相同的索引值。
这是一个错误还是一个功能?我该如何避免这个问题?它不仅限于列表推导......
>>> funcs = []
... for i in range(10):
... funcs.append(lambda:i)
... [j() for j in funcs]
[9, 9, 9, 9, 9, 9, 9, 9, 9, 9]
答案 0 :(得分:14)
lambda
在您调用时返回i
的值。由于您在循环结束后调用lambda
,因此i
的值始终为9.
您可以在lambda中创建一个本地i
变量来保存lambda
定义时的值:
>>> [j() for j in [lambda i=i:i for i in range(10)]]
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
另一个解决方案是创建一个返回lambda
:
def create_lambda(i):
return lambda:i
>>> [j() for j in [create_lambda(i) for i in range(10)]]
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
这是有效的,因为每次调用i
都会创建一个不同的闭包(保持不同的create_lambda
值。)
答案 1 :(得分:9)
你在这里看到的是closures的影响。 lambda正在从稍后使用的程序中捕获状态。因此,虽然每个lambda都是唯一的对象,但状态不一定是唯一的。
此处的实际“gotchya”是捕获变量i
,而不是i
在该时间点表示的值。我们可以用一个更简单的例子来说明这一点:
>>> y = 3
>>> f = lambda: y
>>> f()
3
>>> y = 4
>>> f()
4
lambda保持对变量的引用,并在执行lambda时计算该变量。
要解决此问题,您可以在lambda中分配一个局部变量:
>>> f = lambda y=y:y
>>> f()
4
>>> y = 6
>>> f()
4
最后,在循环的情况下,循环变量只被“声明”一次。因此,循环中对循环变量的任何引用都将持续到下一次迭代。这包括列表推导中的变量。
答案 2 :(得分:1)
问题是你没有在列表理解的每次迭代中捕获i的值,你每次都会捕获变量。
问题是闭包通过引用捕获变量。在这种情况下,您将捕获一个值随时间变化的变量(与所有循环变量一样),因此它在运行时的值与创建时的值不同。
答案 3 :(得分:0)
我不确定这是一个错误还是一个功能,但是发生的事情是lambda:i
在形成lambda函数之前没有评估i。所以,它实际上只是一个评估i的当前值的函数。这是另一个如何发生这种情况的例子。
>>> i=5
>>> x=lambda:i
>>> x()
5
>>> i=6
>>> x()
6
所以,显然,正在发生的事情是同样的事情,除了我在你的例子中要9,因为它按顺序分配到0到9的范围。
我认为没有任何好办法可以避免它。 Python中的Lambda函数非常有限。它本身并不是一种真正的功能性语言。
答案 4 :(得分:0)
我不确定你要用lambda函数做什么。它没有争论......
我认为Python正在生成类似于(i for i in range(10))
的生成器,然后迭代它。在计数10次之后,i
的最终值为9,然后这就是lambda函数返回的内容。
你想制作某种能从[0,9]中产生数字的物体吗?因为如果你想这样做,只需使用xrange(10)
,它返回一个产生数字[0,9]的迭代器。
def f(x):
return x
lst = [f(x) for x in xrange(10)]
print(lst == range(10)) # prints True
注意:我认为尝试命名函数j()
是一个非常糟糕的主意。 Python使用j
来表示复数的虚部,我认为名为j
的函数可能会混淆解析器。我收到了一些尝试运行代码的奇怪错误消息。
答案 5 :(得分:0)
将方括号变成圆括号,如下所示: [j中的j()(lambda:i中范围(10)中的i)] 输出: [0、1、2、3、4、5、6、7、8、9] 元组是不可变的变量