Python:global、local与nonlocal变量

1 local和global变量

先来看一个最简单的Python程序例子:

import numpy as np
n = 2
def func(a):
    b = 1    
    return a + b

print(func(n)) # 3

这里b声明在函数func内,则该变量拥有一个local scope(局部作用域,即在函数内),我们将这类变量称为local(局部)变量。

与之相对的npn这两个变量都在函数之外声明,也即它们都在gobal scope(全局作用域)中,我们将它们global(全局)变量。

如果我们使用IDE在return a+b处打断点并调试,那么可以看到IDE很方便地为我们展示了当前的局部和全局变量(以VSCode为例):

我们在local scope内也可以对global变量进行读取,如下所示:

n = 2
def func(a):
    b = 1    
    return a + b + n # 对全局变量进行读取

print(func(n)) # 5

然而,如果在local scope内对global变量进行修改,比如这样写:

n = 2
def func(a):
    n += 1 # 对全局变量进行修改
    b = 1    
    return a + b + n  
 
print(func(n)) 

运行到n+=1这一语句是就会抛出异常:

UnboundLocalError: local variable 'n' referenced before assignment

此时我们需要用global关键字在局部作用域内声明一个global变量,然后就可以自由修改该变量了:

n = 2
def func(a):
    global n
    n += 1
    b = 1    
    return a + b + n

print(func(n)) # 6

2 nonlocal 变量

最后,我们还要提一种变量,叫nonlocal变量。这种变量常用在局部嵌套函数中将外层函数中的自由变量绑定到内层函数作用域(事实上外层函数中的自由变量对于内层函数来说既也非local也非global,故名nonlocal)。如下列所示:

# outside function 
def outer():
    message = 'local'

    # nested function  
    def inner():

        # declare nonlocal variable
        nonlocal message

        message = 'nonlocal'
        print("inner:", message)

    inner()
    print("outer:", message)

outer()

打印输出:

inner: nonlocal
outer: nonlocal

可以看到,nonlocal的使用将外层函数作用域中的自由变量message和内层函数的作用域进行了绑定。

不过需要注意的是,如果我们使用global关键字来声明变量:

# outside function 
def outer():
    message = 'local'

    # nested function  
    def inner():

        # declare global variable
        global message

        message = 'nonlocal'
        print("inner:", message)

    inner()
    print("outer:", message)

outer()

那么最终的打印输出结果为:

inner: nonlocal
outer: local

这是因为global关键字的使用让我们在inner()函数(局部作用域)内声明了一个global变量,故如果我们在inner()函数内做任何修改,则修改的结果只会在局部作用域(也即outer()函数)之外出现。

此外,nonlocal还可以用来构建如下列所示的闭包函数func(参见我的博客《Python技法4:闭包和保存自由变量 》):

def sample():
    n = 0
    # 闭包函数
    def func():
        nonlocal n
        n += 1
        print("n =", n)
    return func

f = sample()
f() # n = 1
f() # n = 2

这里也是通过nonlocal关键字将自由变量与内层函数绑定,然后再对其进行修改的。

posted @ 2022-11-27 01:01  orion-orion  阅读(462)  评论(0编辑  收藏  举报