嵌套函数中的局部变量

2024-05-23 20:36:36 发布

您现在位置:Python中文网/ 问答频道 /正文

好吧,请原谅我,我知道这看起来很复杂,但请帮助我理解发生了什么。

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“打包”到嵌套函数的本地作用域中吗?如果不是,对嵌套函数的调用如何查找局部变量?

我知道遇到这些问题通常意味着一个人“做错了”,但我想知道会发生什么。


Tags: theselfgetdefpartialdocatmary
3条回答

我的理解是,当实际调用生成的pet_函数时,cage是在父函数名称空间中查找的,而不是以前。

所以当你这么做的时候

funs = list(get_petters())

生成3个函数,找到最后创建的框架。

如果将最后一个循环替换为:

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)的所有调用都返回cats。

嵌套函数在执行时从父作用域中查找变量,而不是在定义时。

编译函数体,验证“free”变量(不是通过赋值在函数本身中定义的),然后将其绑定为函数的闭包单元格,代码使用索引引用每个单元格。pet_function因此有一个自由变量(cage),然后通过闭包单元索引0引用。闭包本身指向get_petters函数中的局部变量cage

当您实际调用函数时,该闭包将用于查看调用函数时周围作用域中cage的值。问题就在这里。当您调用函数时,get_petters函数已经完成计算结果。在执行过程中的某个时刻,cage局部变量被分配给每个'cow''dog''cat'字符串,但是在函数的末尾,cage包含最后一个值'cat'。因此,当您调用每个动态返回的函数时,您将得到打印的值'cat'

解决办法是不要依赖于闭包。您可以使用部分函数,创建新函数作用域,或者将变量绑定为关键字参数的默认值。

  • 部分函数示例,使用^{}

    from functools import partial
    
    def pet_function(cage=None):
        print "Mary pets the " + cage.animal + "."
    
    yield (animal, partial(gotimes, partial(pet_function, cage=cage)))
    
  • 创建新范围示例:

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

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

不需要在循环中定义scoped_cage函数,编译只进行一次,而不是在循环的每次迭代中进行。

相关问题 更多 >