如何在 Python 闭包中捕获值

How to capture a value in a Python Closure

如何在 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'?

我找到了答案here on a blog post by Bob Kerner

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 闭包很奇怪。

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”的中间对象。

你必须考虑当 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。在这种情况下,我们甚至没有创建闭包,我们正在创建函数。在下一次迭代中打印“fish”,在最后一次迭代中打印“zebra”