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
的价值被懒惰地存储为其最终值。
作为一个生成器,函数可以工作(即依次打印每个值),但是当转换为列表时,它将运行在生成器上 ,因此所有对cage
( cage.animal
)的调用cage.animal
返回。