【问题标题】:Generating functions inside loop with lambda expression in python在python中使用lambda表达式在循环内生成函数
【发布时间】:2010-12-22 21:54:35
【问题描述】:

如果我列出两个函数列表:

def makeFun(i):
    return lambda: i

a = [makeFun(i) for i in range(10)]
b = [lambda: i for i in range(10)]

为什么列表ab 不以保存方式运行?

例如:

>>> a[2]()
2
>>> b[2]()
9

【问题讨论】:

标签: python lambda scope


【解决方案1】:

正如其他人所说,范围界定是问题所在。请注意,您可以通过向 lambda 表达式添加一个额外的参数并为其分配一个默认值来解决此问题:

>> def makeFun(i): return lambda: i
... 
>>> a = [makeFun(i) for i in range(10)]
>>> b = [lambda: i for i in range(10)]
>>> c = [lambda i=i: i for i in range(10)]  # <-- Observe the use of i=i
>>> a[2](), b[2](), c[2]()
(2, 9, 2)

结果是i 现在被显式放置在限制为lambda 表达式的范围内。

【讨论】:

    【解决方案2】:

    从技术上讲,lambda 表达式在全局范围内可见的 i关闭,后者最后设置为 9。这是 相同 i 是在所有 10 个 lambdas 中都提到。例如,

    i = 13
    print b[3]()
    

    makeFun 函数中,lambda 在函数调用时定义的i 上关闭。这些是十个不同is。

    【讨论】:

    • 好的,现在这对我来说很有意义。谢谢!
    • 从 Python 3 开始,列表推导式现在有了自己的范围。所以改变它的值不会改变 print(b[3]()) 的输出。有关列表理解行为的更多详细信息,请参见 here
    【解决方案3】:

    一组函数 (a) 对传递的参数进行操作,另一组 (b) 对全局变量进行操作,然后将其设置为 9。检查反汇编:

    >>> import dis
    >>> dis.dis(a[2])
      1           0 LOAD_DEREF               0 (i)
                  3 RETURN_VALUE
    >>> dis.dis(b[2])
      1           0 LOAD_GLOBAL              0 (i)
                  3 RETURN_VALUE
    >>>
    

    【讨论】:

      【解决方案4】:

      增加一些清晰度(至少在我看来)

      def makeFun(i): return lambda: i
      a = [makeFun(i) for i in range(10)]
      b = [lambda: i for i in range(10)]
      

      a 使用 makeFun(i),它是一个带参数的函数。

      b 使用 lambda: i 这是一个没有参数的函数。它使用的 i 和之前的很不一样

      为了使 a 和 b 相等,我们可以让两个函数都使用无参数:

      def makeFun(): return lambda: i
      a = [makeFun() for i in range(10)]
      b = [lambda: i for i in range(10)]
      

      现在两个函数都使用全局 i

      >>> a[2]()
      9
      >>> b[2]()
      9
      >>> i=13
      >>> a[2]()
      13
      >>> b[2]()
      13
      

      或者(更有用)使两者都使用一个参数:

      def makeFun(x): return lambda: x
      a = [makeFun(i) for i in range(10)]
      b = [lambda x=i: x for i in range(10)]
      

      我故意将 i 更改为 x ,其中变量是本地的。 现在:

      >>> a[2]()
      2
      >>> b[2]()
      2
      

      成功!

      【讨论】:

        【解决方案5】:

        python 中的 Lambda 共享创建它们的变量范围。在第一种情况下,lambda 的范围是 makeFun 的。在第二种情况下,它是全局 i,它是 9,因为它是循环的剩余部分。

        反正我是这么理解的……

        【讨论】:

          【解决方案6】:

          不错的收获。列表理解中的 lambda 每次都看到相同的本地 i

          你可以改写为:

          a = []
          for i in range(10):
              a.append(makefun(i))
          
          b = []
          for i in range(10):
              b.append(lambda: i)
          

          结果相同。

          【讨论】:

          • 我不确定你想说什么。在您的示例中,b[2]() 也返回 9。
          • 我是说它等同于列表推导;不是说a和b一样。我认为拼写出的for 循环可以更容易地查看i 的来源。
          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2014-08-15
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多