【问题标题】:Getting confused with lambda and list comprehension对 lambda 和列表理解感到困惑
【发布时间】:2012-03-01 15:00:13
【问题描述】:

使用以下语法阅读有关堆栈溢出的问题

In [1]: [lambda: x for x in range(5)][0]()
Out[1]: 4
In [2]: [lambda: x for x in range(5)][2]()
Out[2]: 4

但是我很难理解为什么这个输出是 4, 我的理解是它总是将列表的最后一个值作为输出,

In [4]: [lambda: x for x in [1,5,7,3]][0]()
Out[4]: 3

但仍然不相信这个语法是如何以最后一个值结束的。

如果我能得到这个语法的正确解释,我会很高兴

【问题讨论】:

  • 你认为你能找到你提到的那个问题吗?这很有趣,我也想弄清楚:)
  • In [84]: [z() for z in (lambda: x for x in range(5))] Out[84]: [0, 1, 2, 3, 4] In [85]: [z() for z in [lambda: x for x in range(5)]] Out[85]: [4, 4, 4, 4, 4] 如果使用生成器而不是列表理解,那么输出也会不同!
  • @ktodisco:我很确定这是我的问题:stackoverflow.com/questions/9189702/…
  • @Neil G:是的,就是这个.... :)

标签: python lambda closures list-comprehension


【解决方案1】:

对于具有 n 次迭代的 LC,x 被分配源序列的元素 0 到 n-1。在最后一次迭代中,x 被分配到最后一个元素。需要注意的一点是它总是相同的x,并且在最后调用该函数会返回最后保存的x

【讨论】:

  • 您能否将代码分成多个步骤,以便我更好地理解它:)
  • @avasal lambda 包含 name x,而不是 x 的值。当 LC 完成时,您将获得一个 lambda 列表,返回绑定到名称 x 的值 - 最新值。
  • 如果 LC 完成第一个并且最后一个值返回给 lambda ,我很困惑为什么在 [6] 中有 5 个 lambda 函数:[lambda: x for x in range(5)] Out [6]: [ at 0x16b7ed8>, at 0x16b7e60>, at 0x16c10c8>, at 0x16c1140>, at 0x16c11b8>]
  • 因为仍然创建了 5 个 lambda;这就是 LC 正在做的事情。
  • 就像[lambda: globals().get('x') for x in [1,5,7,3,5]][3]()
【解决方案2】:

这将解决它:

[(lambda(i): lambda: i)(x) for x in range(5)][2]()

问题在于,您没有在列表理解的每次迭代中捕获 x 的 ,而是每次都在捕获 变量。 p>

【讨论】:

  • 我不需要解决这个问题,我需要了解那里发生了什么:)
  • 我认为 OP 的问题是了解当他没有捕获 x 的值时 python 如何解释。 :)
【解决方案3】:

这实际上与列表推导或 lambdas 无关。这是关于 Python 中的作用域规则。让我们将列表推导式重写为等效循环:

funcs = []
for x in range(5):
    def f(): return x
    funcs.append(f)
funcs[0]() # returns 4

在这里,我们可以看到我们依次构造函数并将它们存储在一个列表中。当调用这些函数之一时,所发生的只是查找并返回 x 的值。但是x 是一个值变化的变量,所以4 的最终值总是返回的。您甚至可以在循环之后更改 x 的值,例如,

x = 32 
funcs[2]() # returns 32

为了获得您所期望的行为,Python 需要将for 的内容限定为一个块;它没有。通常,这不是问题,并且很容易解决。

【讨论】:

    【解决方案4】:

    为了我的理解,让我破解代码

    In [39]: [lambda: x for x in [1,5,7,3]]
    Out[39]: 
    [<function <lambda> at 0x2cd1320>,
     <function <lambda> at 0x2cd12a8>,
     <function <lambda> at 0x2cd10c8>,
     <function <lambda> at 0x2cd1050>]
    

    上面给出了函数列表

    In [40]: [lambda: x for x in [1,5,7,3]][1]
    Out[40]: <function <lambda> at 0x2cd1488>
    

    索引 1 给出了函数列表中的 1 个函数。

    现在此函数将应用于 x,它始终具有列表的最后一个值。那就是 y 总是给出最后一个值作为结果。

    就像下面的代码。

    In [41]: [lambda: 2][0]()
    Out[41]: 2
    
    
    In [42]: alist = [1,5,7,3,4,5,6,7]
    

    x for x in [1,5,7,3] 等价于下面的函数 f(x)。 和
    lambda: x for x in [1,5,7,3] 等价于 lambda: 3

    In [43]: def f(x):
       ....:     for x in alist:
       ....:         pass
       ....:     return x
    In [44]: print f(alist)
    7
    

    【讨论】:

      【解决方案5】:

      正如其他人所说,您遇到了常见的闭环问题;即闭包通过引用捕获变量的问题。在这种情况下,您正在捕获一个其值随时间变化的变量(与所有循环变量一样),因此运行它时它的值与创建它时的值不同。

      要完成您想要的,您需要在创建 lambda 时捕获变量的值。 sblom 建议将其包装在立即执行的函数中的解决方案:

      [(lambda(i): lambda: i)(x) for x in range(5)][2]()
      

      另一个在 Python 中经常看到的解决方案是使用默认参数,它也会在创建时评估值:

      [lambda i=x: i for x in range(5)][2]()
      

      (通常写成lambda x=x: x,但为了清楚起见我重命名了变量)

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2018-11-30
        • 2013-05-15
        • 2012-03-22
        • 2018-08-30
        • 2018-11-13
        • 2021-10-22
        相关资源
        最近更新 更多