Python闭包中的可变绑定时间

2024-03-28 20:04:23 发布

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

从Python的Programming FAQ来看,这并不像许多人期望的那样有效:

>>> squares = []
>>> for x in range(5):
...     squares.append(lambda: x**2)
>>> squares[2]() # outputs 16, not 4

提供以下解释:

This happens because x is not local to the lambdas, but is defined in the outer scope, and it is accessed when the lambda is called --- not when it is defined.

这很好。然而,对我来说,这个解释听起来并不完全正确。考虑这一点:

def buildFunction():
    myNumber = 6 # Won't compile without this line
    def fun():
        return myNumber
    return fun

def main():
    myNumber = 3 # Ignored
    myFun = buildFunction()
    myNumber = 3 # Ignored
    print(myFun()) # prints 6, not 3

这是有道理的,但是有人能提供一个更准确/通用的定义来说明何时发生Python闭包的变量绑定吗


Tags: thelambdainreturnisdefnotit
2条回答

每个变量都有一个作用域。 名称查找(获取名称指向的值)默认解析为最内部的范围。 只能重写本地作用域或包含作用域中的名称(使用关键字nonlocalglobal)。 在main中的第二个示例中,您将myNumber分配给fun函数可以访问的不同范围

squares = []
 for x in range(5):
    squares.append(lambda: x**2) # scope = global / lambda
print(squares[2]())

def buildFunction():
    myNumber = 6 # scope = global / buildFunction
    def fun():
        return myNumber # scope = global / buildFunction / fun
    return fun

myNumber = 1 # scope = global
def main():
    myNumber = 3 # scope = global / main. Out of scope for global / buildFunction
    myFun = buildFunction()
    print(myFun())

调用fun时,Python查看fun的局部作用域,但找不到myNumber。 所以它查看它的包含范围。 buildFunction作用域在作用域(myNumber = 6)中有myNumber,因此fun返回6

如果buildFunction在作用域中没有myNumber,那么Python将查看下一个作用域。 在我的版本中,全局作用域有myNumber = 1 所以fun将返回1

如果myNumber在全局作用域中也不存在,则会引发NameError,因为在本地作用域或其任何包含作用域中都找不到myNumber

考虑如下:

def main():
    def buildFunction():
        def fun():
            return myNumber

        return fun

    myNumber = 3  # can use this...
    myFun = buildFunction()
    myNumber = 3  # ... or this
    print(myFun())  # prints 3 this time

这与lambda示例更具可比性,因为闭包函数嵌套在声明感兴趣变量的范围内。您的示例有两个不同的作用域,因此有两个完全不相关的myNumbers

如果您没有遇到nonlocal关键字,您能猜出这将打印什么吗

def main():
    def buildFunction():
        nonlocal myNumber
        myNumber = 6

        def fun():
            return myNumber

        return fun

    myNumber = 3
    myFun = buildFunction()
    # myNumber = 3
    print(myFun())

相关问题 更多 >