Python 3 中的 nonlocal 语句

February 26, 2015

最近在看 Python 3,其中变量作用域增加了 nonlocal 关键字,用来指定非全局、非本地作用域外的封闭作用域中的变量。

nonlocal_stmt ::=  "nonlocal" identifier ("," identifier)*

nonlocal语句会让所列标示符identifier引用之前的最近封闭作用域的绑定变量,除了全局作用域外。 但是nonlocal必须引用之前封闭作用域存在的变量(global则可以声明不存在的)。 而且nonlocal语句所声明的标示符不能与已有的本地变量重名。

例子:

def scope_test():
    def do_local():
        spam = "local spam"
    def do_nonlocal():
        nonlocal spam
        spam = "nonlocal spam"
    def do_global():
        global spam
        spam = "global spam"

    spam = "test spam"
    do_local()
    print("After local assignment:", spam)
    do_nonlocal()
    print("After nonlocal assignment:", spam)
    do_global()
    print("After global assignment:", spam)

scope_test()
print("In global scope:", spam)

输出结果是:

After local assignment: test spam
After nonlocal assignment: nonlocal spam
After global assignment: nonlocal spam
In global scope: global spam

注意局部的赋值 (默认) 并没有改变 scope_test 绑定的 spam. 而 nonlocal 则改变了 scope_test 中的 spam, 而 global 则改变了模块级别的绑定。 可以看到在 global 赋值之前并没有绑定 spam 的值.

有了 nonlocal 语句,Python的闭包就不只能只读访问非本地作用域的变量了。

def foo(num):
    count = 0
    def bar():
        nonlocal count
        nonlocal num
        num += 1
        count += 1
        print("num is %d" % num)
        print("bar invoke %d times" % count)
    return bar

func = foo(10)

func()
func()

运行结果:

num is 11
bar invoke 1 times
num is 12
bar invoke 2 times

参考文献:

The nonlocal statement

Python 的作用域和命名空间


Profile logo

Written by Alvie Zhang

If you're going through hell, keep going