(lambda)函数闭包在Python中捕获什么? [英] What do (lambda) function closures capture in Python?

查看:340
本文介绍了(lambda)函数闭包在Python中捕获什么?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

最近,我开始使用Python玩,我发现了闭包工作方式中的一些奇怪的东西。考虑下面的代码:

  adders = [0,1,2,3] 
for i in [ 1,2,3]:
adders [i] = lambda a:i + a

print adders [1](3)
/ pre>

它构建一个简单的函数数组,它接受单个输入并返回由数字添加的输入。函数在for循环中构造,其中迭代器 i 从0到3.对于这些数字中的每一个,创建λ函数,捕获 i 并将其添加到函数输入。最后一行调用具有3作为参数的第二个lambda函数。令我惊讶的是,输出是:

  6 

我预期一个4.我的推理是:在Python中,一切都是一个对象,因此每个变量是必不可少的指针。当为 i 创建lambda闭包时,我希望它存储一个指向 i 当前指向的整数对象的指针。这意味着当 i 分配一个新的整数对象时,它不应该影响以前创建的闭包。可悲的是,检查调试器中的adders数组显示它。所有lambda函数引用 i ,3的最后一个值,这导致adders1返回6.



其中:




  • 闭包捕获的是什么?

  • 什么是最优雅的方式来说服lambda函数捕获当前值 i ,并且在 i 更改其值时不会受到影响。
  • 您的第二个问题已被回答,但是您的第一个问题:


    关闭捕获什么?


    Python是动态和词汇。闭包将永远记住变量的名称和范围,而不是它指向的对象。由于您示例中的所有函数都在同一范围内创建,并使用相同的变量名,因此它们总是引用相同的变量。



    strong>关于你如何克服这个问题的另一个问题,有两种方式来想:


    1. 最简洁,但不完全等效的方式是 Adrien Plisson推荐的。创建一个带有额外参数的lambda,并将额外参数的默认值设置为您要保留的对象。


    2. 稍微冗长一点,每次创建lambda时都创建一个新作用域:

       >> adders = [0,1,2,3] 
      >>>> for i in [0,1,2,3]:
      ... adders [i] =(lambda b:lambda a:b + a)(i)
      ...
      >>>> adders [1](3)
      4
      >>> adders [2](3)
      5

      (一个lambda,为了简洁),它绑定其参数,并传递要绑定的值作为参数。在实际代码中,你很可能有一个普通的函数,而不是lambda来创建新的范围:

        def createAdder (x):
      return lambda y:y + x
      adders = [createAdder(i)for i in range(4)]



    Recently I started playing around with Python and I came around something peculiar in the way closures work. Consider the following code:

    adders= [0,1,2,3]
    for i in [0,1,2,3]:
       adders[i]=lambda a: i+a
    
    print adders[1](3)
    

    It builds a simple array of functions that take a single input and return that input added by a number. The functions are constructed in for loop where the iterator i runs from 0 to 3. For each of these number a lambda funciton is created which captures i and adds it to the function's input. The last line calls the second lambda function with 3 as a parameter. To my surprise the output was:

    6
    

    I expected a 4. My reasoning was: in Python everything is an object and thus every variable is essential a pointer to it. When creating the lambda closures for i, I expected it to store a pointer to the integer object currently pointed to by i. That means that when i assigned a new integer object it shouldn't effect the previously created closures. Sadly, inspecting the adders array within a debugger shows that it does. All lambda functions refer to the last value of i, 3, which results in adders1 returning 6.

    Which me the following:

    • what does the closures capture exactly?
    • What is the most elegant way to convince that lambda functions to capture the current value of i and in a way that will not be affected when i changes it's value.

    解决方案

    Your second question has been answered, but as for your first:

    what does the closure capture exactly?

    Scoping in Python is dynamic and lexical. A closure will always remember the name and scope of the variable, not the object it's pointing to. Since all the functions in your example are created in the same scope and use the same variable name, they always refer to the same variable.

    EDIT: Regarding your other question of how to overcome this, there are two ways that come to mind:

    1. The most concise, but not strictly equivalent way is the one recommended by Adrien Plisson. Create a lambda with an extra argument, and set the extra argument's default value to the object you want preserved.

    2. A little more verbose but less hacky would be to create a new scope each time you create the lambda:

      >>> adders = [0,1,2,3]
      >>> for i in [0,1,2,3]:
      ...     adders[i] = (lambda b: lambda a: b + a)(i)
      ...     
      >>> adders[1](3)
      4
      >>> adders[2](3)
      5
      

      The scope here is created using a new function (a lambda, for brevity), which binds its argument, and passing the value you want to bind as the argument. In real code, though, you most likely will have an ordinary function instead of the lambda to create the new scope:

      def createAdder(x):
          return lambda y: y + x
      adders = [createAdder(i) for i in range(4)]
      

    这篇关于(lambda)函数闭包在Python中捕获什么?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆