【问题标题】:How to capture a value in a Python Closure如何在 Python 闭包中捕获值
【发布时间】:2021-11-18 10:28:02
【问题描述】:

如何在 python 闭包中捕获值(而不是引用)?

我尝试过的:

这是一个创建无参数函数列表的函数,每个函数都输出一个字符串:

def makeListOfFuncs( strings):
  list = []
  for str in strings:
    def echo():
      return str
    list.append( echo)
  return list

如果 python 中的闭包像其他所有语言一样工作,我希望这样的调用:

for animalFunc in makeListOfFuncs( ['bird', 'fish', 'zebra']):
  print( animalFunc())

... 会产生如下输出:

bird
fish
zebra

但在 python 中,我们得到:

zebra
zebra
zebra

显然,echo 函数的闭包正在捕获对 str 的引用,而不是在闭包构造时调用帧中的值。

如何定义 makeListOfFuncs,以便得到 'bird'、'fish'、'zebra'?

【问题讨论】:

    标签: python-3.x closures


    【解决方案1】:

    如果你知道 Python 内部的闭包是如何工作的,那么 Python 闭包并不奇怪。

    def makeListOfFuncs( strings):
      list = []
      for str in strings:
        def echo():
          return str
        list.append( echo)
      return list
    

    您正在返回一个闭包列表。变量“str”在作用域之间共享,它在两个不同的作用域中。当python看到它时,它会创建一个中间对象。此对象包含对另一个对象“str”的引用。对于每个闭包,它都是同一个单元格。你可以测试一下:

    closures_list= makeListOfFuncs( ['bird', 'fish', 'zebra'])
     # Those will return same cell address
     closures_list[0].__closure__
     closures_list[1].__closure__
     closures_list[2].__closure__
    

    当您调用makeListOfFuncs 时,它将运行for 循环,并且在循环结束时,中间对象将指向“zebra”。因此,当您调用每个闭包 print( animalFunc()) 时,它将访问引用“zebra”的中间 obj。

    你必须想一想当 python 创建一个函数以及它对它求值时会发生什么。

    def echo():
      return str
    

    我们需要以某种方式捕获“str”的值作为正在创建的函数。因为如果你等到函数被评估,那就太晚了。另一种解决方案是定义一个默认值:

    def makeListOfFuncs( strings):
        list = []
        for str in strings:
            def echo(y=str):
                return y
            list.append( echo)
        return list
    
    for animalFunc in makeListOfFuncs( ['bird', 'fish', 'zebra']):
        print( animalFunc())
    

    此解决方案有效,因为默认值会在创建时进行评估。所以在创建echo 时,将使用默认值“str”。默认值不会指向单元格。

    在第一次迭代中,默认值为“bird”。 Python 不会将此视为free variable。在这种情况下,我们甚至没有创建闭包,我们正在创建函数。在下一次迭代“鱼”和最后一次迭代“斑马”将被打印

    【讨论】:

      【解决方案2】:

      我在Bob Kerner 的博客文章中找到了答案here

      def makeListOfFuncs( strings):
        list = []
        for str in strings:
          def generateFunc( str):
            def echo():
              return str
            return echo
          list.append( generateFunc( str))
        return list
      
      for animalFunc in makeListOfFuncs( ['bird', 'fish', 'zebra']):
        print( animalFunc())
      

      Python 闭包很奇怪。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2016-10-16
        • 1970-01-01
        相关资源
        最近更新 更多