Python 循环中奇怪的lambda行为
我偶然发现python中的一个行为,我很难理解。这是概念验证代码:Python 循环中奇怪的lambda行为,python,python-2.7,scope,variable-assignment,lambda,Python,Python 2.7,Scope,Variable Assignment,Lambda,我偶然发现python中的一个行为,我很难理解。这是概念验证代码: from functools import partial if __name__ == '__main__': sequence = ['foo', 'bar', 'spam'] loop_one = lambda seq: [lambda: el for el in seq] no_op = lambda x: x loop_two = lambda seq: [partial(no_op,
from functools import partial
if __name__ == '__main__':
sequence = ['foo', 'bar', 'spam']
loop_one = lambda seq: [lambda: el for el in seq]
no_op = lambda x: x
loop_two = lambda seq: [partial(no_op, el) for el in seq]
for func in (loop_one, loop_two):
print [f() for f in func(sequence)]
上面的输出是:
['spam', 'spam', 'spam']
['foo', 'bar', 'spam']
loop\u one
的行为令我惊讶,因为我希望它的行为与loop\u two
一样:el
是一个不可变的值(字符串),在每个循环中都会发生变化,但lambda
似乎存储了指向“循环变量”的指针,就像循环为序列的每个元素循环相同的内存地址一样
上述行为与包含for循环的完整函数相同(因此它不是列表理解语法)
但是等等:还有更多。。。更令人费解的是强>
以下脚本的工作方式类似于loop\u one
:
b = []
for foo in ("foo", "bar"):
b.append(lambda: foo)
print [a() for a in b]
(输出:['bar','bar']
)
但请注意,如果将变量名foo
替换为a
,会发生什么情况:
b = []
for a in ("foo", "bar"):
b.append(lambda: a)
print [a() for a in b]
(输出:[,]
)
你知道这里发生了什么吗?我怀疑我的解释器的底层C实现一定有一些问题,但我没有其他任何东西(Jthon、PyPy或类似的东西)来测试这种行为在不同的实现中是否一致。在创建lambda时,变量(foo
在下面的示例中)没有绑定,但是当lambda被调用时
>>> b = []
>>> for foo in ("foo", "bar"):
... b.append(lambda: foo)
...
>>> foo = "spam"
>>> print [a() for a in b]
['spam', 'spam']
loop\u one
中使用的函数lambda:el
指的是未在本地范围内定义的变量el
。因此,Python接下来会在另一个lambda
的封闭范围中查找它:
lambda seq: [lambda: el for el in seq]
按照所谓的
当调用lambda:el
时,这个封闭的lambda(当然)已经被调用,并且列表理解已经得到评估。列表理解中使用的el
是此封闭lambda中的局部变量。它的值是Python在lambda:el
中查找el
的值时返回的值。对于列表中所有不同的lambda:el
函数,el
的值都是相同的:它是在循环中为seq
中的el分配给el
的最后一个值。因此,el
始终是'spam'
,是seq
中的最后一个值
您已经找到了一种解决方法,可以使用诸如
循环\u two
之类的闭包。另一种方法是将el
定义为具有默认值的局部变量:
loop_one = lambda seq: [lambda el=el: el for el in seq]
loop_one = lambda seq: [lambda el=el: el for el in seq]