This question already has answers here:
Local variables in nested functions

(4个答案)


7年前关闭。




我偶然发现了我很难理解的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_one的行为令我感到惊讶,因为我希望它的行为像loop_two一样:el是一个不变的值(字符串),在每个循环中都会更改,但是 lambda似乎存储了指向“循环变量” 的指针,例如循环是否为序列的每个元素循环使用相同的内存地址。

上面的行为与其中带有for循环的成熟函数相同(因此,它不是列表理解语法)。

但是,请耐心等待:还有更多……还有更多令人困惑的地方!

以下脚本的工作方式类似于loop_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]

(输出:[<function <lambda> at 0x25cce60>, <function <lambda> at 0x25cced8>])

对这里发生的事情有任何想法吗?我怀疑肯定有一些与我的解释器的底层C实现有关的陷阱,但是我没有别的东西(Jthon,PyPy或类似的东西)来测试这种行为在不同实现之间是否一致。

最佳答案

lambda: el中使用的函数loop_one引用了在本地范围中 undefined variable el。因此,Python在另一个lambda的封闭范围内寻找它:

lambda seq: [lambda: el for el in seq]

按照所谓的LEGB rule

lambda: el调用时,这个封闭的lambda(当然)已经被调用,并且列表理解已经得到评估。列表推导中使用的el是此封闭lambda中的局部变量。它的值是当Python在el中查找lambda: el的值时返回的值。对于列表理解中所有不同的el函数,lambda: el的值是相同的:这是el循环中分配给for el in seq的最后一个值。因此,el始终是'spam',是seq中的最后一个值。

您已经找到了一种解决方法,可以使用诸如loop_two之类的闭包。另一种方法是将el定义为具有默认值的局部变量:
loop_one = lambda seq: [lambda el=el: el for el in seq]

关于python - 循环中的奇怪lambda行为,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/19768456/

10-16 17:29