Python嵌套函数中的局部variables

好吧,对此我忍无可忍,我知道这样看起来会非常复杂,但请帮我理解发生了什么。

from functools import partial class Cage(object): def __init__(self, animal): self.animal = animal def gotimes(do_the_petting): do_the_petting() def get_petters(): for animal in ['cow', 'dog', 'cat']: cage = Cage(animal) def pet_function(): print "Mary pets the " + cage.animal + "." yield (animal, partial(gotimes, pet_function)) funs = list(get_petters()) for name, f in funs: print name + ":", f() 

得到:

 cow: Mary pets the cat. dog: Mary pets the cat. cat: Mary pets the cat. 

所以基本上,为什么我没有得到三种不同的动物? cage不是被“包装”到嵌套函数的本地范围中吗? 如果没有,如何调用嵌套函数查找局部variables?

我知道遇到这样的问题通常意味着“做错了”,但我想知道发生了什么。

嵌套函数在执行时从父范围查找variables,而不是在定义时查找。

函数体被编译,并且“自由”variables(未在函数本身中被赋值)被validation,然后被绑定为函数的闭包,代码使用索引来引用每个单元格。 因此pet_function一个自由variables( cage ),然后通过一个闭合单元索引0来引用它。闭包本身指向get_petters函数中的局部variablescage

实际调用该函数时,在调用该函数时,该闭包将用于查看周围范围内的cage值。 这就是问题所在。 当你调用你的函数时, get_petters函数已经完成了计算结果。 在执行过程中某个点的cage局部variables被赋予了每个'cow''dog''cat'string,但在函数结束时, cage包含最后一个值'cat' 。 因此,当您调用每个dynamic返回的函数时,都会打印出'cat'值。

解决方法是不依靠closures。 您可以改为使用部分函数 ,创build新的函数作用域 ,或将该variables绑定为关键字参数默认值

  • 部分函数示例,使用functools.partial()

     from functools import partial def pet_function(cage=None): print "Mary pets the " + cage.animal + "." yield (animal, partial(gotimes, partial(pet_function, cage=cage))) 
  • 创build一个新的作用域示例:

     def scoped_cage(cage=None): def pet_function(): print "Mary pets the " + cage.animal + "." return pet_function yield (animal, partial(gotimes, scoped_cage(cage))) 
  • 将variables绑定为关键字参数的默认值:

     def pet_function(cage=cage): print "Mary pets the " + cage.animal + "." yield (animal, partial(gotimes, pet_function)) 

没有必要在循环中定义scoped_cage函数,编译只发生一次,而不是循环的每次迭代。

我的理解是,实际调用了yield的pet_function时,在父函数名称空间中查找的是cage,而不是之前。

所以,当你这样做

 funs = list(get_petters()) 

您生成了3个函数,它们将查找最后创build的笼子。

如果你用最后一个循环replace:

 for name, f in get_petters(): print name + ":", f() 

你会得到:

 cow: Mary pets the cow. dog: Mary pets the dog. cat: Mary pets the cat. 

这源于以下

 for i in range(2): pass print i is 1 

迭代后, i的价值被懒惰地存储为其最终值。

作为一个生成器,函数可以工作(即依次打印每个值),但是当转换为列表时,它将运行在生成器上 ,因此所有对cagecage.animal )的调用cage.animal返回。