嵌套函数中的局部变量 [英] Local variables in nested functions

查看:185
本文介绍了嵌套函数中的局部变量的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

好的,请耐心等待,我知道它看起来会令人费解,但是请帮助我了解发生了什么.

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是否不是打包"到嵌套函数的本地范围中的?如果没有,对嵌套函数的调用如何查找局部变量?

我知道遇到这类问题通常意味着一个人做错了",但我想了解会发生什么.

解决方案

嵌套函数在执行时(而不是在定义时)从父范围中查找变量.

编译函数主体,并验证自由"变量(未在赋值中在函数本身中定义),然后将其作为闭包单元绑定到函数,并且代码使用索引引用每个单元格. pet_function因此具有一个自由变量(cage),该变量随后通过闭包单元索引0进行引用.闭包本身指向get_petters函数中的局部变量cage.

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

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

  • 使用 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)))
    

  • 创建一个新的范围示例:

    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函数,编译仅进行一次,而不是在循环的每次迭代中进行.

Okay, bear with me on this, I know it's going to look horribly convoluted, but please help me understand what's happening.

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()

Gives:

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

So basically, why am I not getting three different animals? Isn't the cage 'packaged' into the local scope of the nested function? If not, how does a call to the nested function look up the local variables?

I know that running into these kind of problems usually means one is 'doing it wrong', but I'd like to understand what happens.

解决方案

The nested function looks up variables from the parent scope when executed, not when defined.

The function body is compiled, and the 'free' variables (not defined in the function itself by assignment), are verified, then bound as closure cells to the function, with the code using an index to reference each cell. pet_function thus has one free variable (cage) which is then referenced via a closure cell, index 0. The closure itself points to the local variable cage in the get_petters function.

When you actually call the function, that closure is then used to look at the value of cage in the surrounding scope at the time you call the function. Here lies the problem. By the time you call your functions, the get_petters function is already done computing it's results. The cage local variable at some point during that execution was assigned each of the 'cow', 'dog', and 'cat' strings, but at the end of the function, cage contains that last value 'cat'. Thus, when you call each of the dynamically returned functions, you get the value 'cat' printed.

The work-around is to not rely on closures. You can use a partial function instead, create a new function scope, or bind the variable as a default value for a keyword parameter.

  • Partial function example, using 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)))
    

  • Creating a new scope example:

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

  • Binding the variable as a default value for a keyword parameter:

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

There is no need to define the scoped_cage function in the loop, compilation only takes place once, not on each iteration of the loop.

这篇关于嵌套函数中的局部变量的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆