【问题标题】:List or generator of lambda funcs do not return the same result in Pythonlambda 函数的列表或生成器在 Python 中不返回相同的结果
【发布时间】:2016-05-17 18:34:19
【问题描述】:

当我这样做时:

def create_funcs():
    return (lambda: i for i in range(5))

for f in create_funcs():
    print(f())

我得到了预期:

1
2
3
4

但是当我这样做时:

def create_funcs():
    return [lambda: i for i in range(5)]

for f in create_funcs():
    print(f())

我有点奇怪:

4
4
4
4

谁能解释一下为什么?

【问题讨论】:

  • @BrenBarn:对不起,我不明白另一个问题的答案与我的关系。
  • 特别关注this answer
  • @BrenBarn:好的,但我在 Python3.5 上,为什么会泄漏?
  • 我意识到您的问题与那个问题略有不同,因为您的理解包含一个 lambda。我在下面给出了答案。

标签: python python-3.x functional-programming


【解决方案1】:

您的lambda 函数从周围的作用域捕获变量i。如this question 中所述,范围是理解的范围。

您的循环一次遍历生成器中的项目,并在推进生成器之前调用每个项目。当你得到第一个函数时,生成器在i为0的状态下挂起,所以这就是函数看到的值。当您推进生成器时,它会将i 设置为 1,然后您将获得一个获取该值的函数。生成器“等待”更改i,直到您进入下一个函数。

在列表推导中,您可以一次获得所有功能。但是所有人仍然在理解范围内引用相同的变量i。由于列表推导立即一直到最后,i 在您有机会使用任何函数之前一直到 4。

在这两种情况下,您的 lambda 都引用了理解范围内的变量。只是在生成器理解中,这个变量直到你已经调用了前一个函数之后才会前进。如果您存储第一个函数,您可以看到与列表理解情况类似的行为,但在您再次推进生成器之前不要调用它:

def create_funcs():
    return (lambda: i for i in range(5))

gen = create_funcs()
f1 = next(gen)
f2 = next(gen)
print(f1())
print(f2())

输出是

1
1

由于我改进了生成器,i 为 1,因此 f1f2 在调用它们时都会看到该值。如果你执行for f in list(create_funcs()),你可以看到同样的事情,强制生成器像列表理解一样一直走到最后。

【讨论】:

  • 我明白了。如果我将 lambda: i 替换为 lambda: int(i) 它会起作用,因为我不再直接引用 i 了。感谢您的耐心等待。
  • @JacquesGaudin:使用int(i) 不应该改变任何东西。
  • lambda: int(i) 的行为方式应该与您当前的代码完全相同。如果您想解决此问题,请使用lambda i=i: i。这会将创建 lambda 时的 i 的值绑定到 lambda 自己的命名空间中的默认值。
  • 糟糕!我没有意识到我正在使用生成器功能。是的,它不会改变任何东西,因为 i 仅在调用函数时进行评估。
  • @Blckknght:这很有趣!在 lambda 中使用默认参数将值存储在函数中!这是对默认参数的创造性使用...无论我喜欢与否,我都无法下定决心:)
【解决方案2】:

正如 BrenBarn 所暗示的,列表推导中的变量 i 是推导范围的局部变量。如果您使用括号,这将起作用:

def create_funcs():
    return (lambda: i for i in range(5))

for f in create_funcs():
    print(f())

0
1
2
3
4

【讨论】:

  • 他已经在问题的第一个示例中这样做了。他的问题是为什么这两种情况下的行为不同。
猜你喜欢
  • 2020-07-28
  • 2017-10-01
  • 1970-01-01
  • 2020-09-07
  • 2013-07-22
  • 2014-03-17
  • 1970-01-01
  • 2020-12-29
  • 1970-01-01
相关资源
最近更新 更多