Python中列表解析的闭包

fan*_*656 4 python closures list-comprehension

在列表理解中使用闭包时,我发现了这个:

xs = [1, 2, 3]
fs = [lambda: _ for _ in xs]
vs = [_() for _ in fs]
print vs           # [<function <lambda> at 0x020324B0>, <function <lambda> at 0x020D6AB0>, <function <lambda> at 0x020D6AF0>]
print vs[0]        # <function <lambda> at 0x020324B0>
print vs[0]()      # <function <lambda> at 0x020D6AF0>
print vs[0]()()    # <function <lambda> at 0x020D6AF0>
print vs[0]()()()  # <function <lambda> at 0x020D6AF0>
Run Code Online (Sandbox Code Playgroud)

不应该vs包含ints而不是lambdas?

但是如果我们在两个列表推导中使用不同的名称,它将按预期工作:

xs = [1, 2, 3]
fs = [lambda: x for x in xs]
vs = [f() for f in fs]
Run Code Online (Sandbox Code Playgroud)

我在Python2.7.5和Python2.7.8中进行了测试,两者都给出了相同的结果.(Python3.4.1按预期工作)是否有解释或是一个错误?

Mar*_*ers 6

有两件事情发生了:

  1. 关闭时间晚了; 当你调用lambda时,它将_从关闭的命名空间中获取.它不会_你创建lambda对象时的值.

    请参阅什么(lambda)函数闭包捕获?

  2. Python 2没有为列表推导使用单独的命名空间,Python 3也是如此.

    即使在理解范围之后,也请参阅Python列表理解重新绑定名称.这是正确的吗?

因此,_从您执行列表推导的命名空间中获取,并且当您在vs列表推导中执行lambda时,_绑定到您尝试执行的lambda(作为循环目标),而不是最后一个值来自xs.

在Python 3中,它的工作原理是因为除了可以迭代到列表推导的原始输入之外,所有名称都使用了新的命名空间.有_住在列表理解的新范围,从而保持结合3.

通过在第二次尝试中为循环目标使用不同的名称,您不会屏蔽已_关闭的名称,_仍然绑定到3.