Python 循环中奇怪的lambda行为

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,

我偶然发现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, 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]