详细举例讲解 python 的 global 和 nonlocal 的区别

区别

两个关键词都用于允许在一个局部作用域中使用外层的变量。

  • global 表示将变量声明为全局变量
  • nonlocal 表示将变量声明为外层变量(外层函数的局部变量,而且不能是全局变量)

原理

1、 python 在访问一个变量时,先要去定位这个变量来源于哪里。
python引用变量的顺序如下:

  1. 当前作用域局部变量
  2. 外层作用域变量
  3. 当前模块中的全局变量
  4. python内置变量

即优先从局部作用域中查找这个变量,如果没有的话,再去外层找,如果到了最后还没找到,则报错。

2、python 在新建一个变量时,默认作用域为当前局部作用域。

总结与实例

1、内部函数,不修改外层变量可以访问外层变量。

a = 1
def fun():
    print(a) # 在函数内部找不到对 a 的定义,则去外层查询。输出1。
fun()

2、内部函数,修改同名外层变量,则python会认为是定义了一个新的局部变量,与外层变量无关了。

a = 1
def fun():
    a = 2 # 声明了一个局部变量,与外面等于1的那个a没有关系了
    print(a) # 输出2
fun()
print(a) # 输出1

3、在内部函数修改同名全局变量之前调用变量名称,则引发Unbound-LocalError

a = 1
def fun():
    print(a) # 先引用
    a = 2 # 再修改
fun()

报错:

File “C:\Users\GongYanshang\Desktop\untitled-1.py”, line 3, in fun
print(a)
builtins.UnboundLocalError: local variable ‘a’ referenced before assignment

错误表示,局部变量 a 在定义之前就引用了。
关键在于,报错的是 print(a) 这一行。结合第1个例子,只是增加了一行 a = 2,但是却是 print(a) 报错。这说明在 print(a) 之前,python 已经知道了 a 是一个局部变量,只能从局部作用域中查找。从哪里知道的? 从 a = 2 这一行知道的。
这时候,如果我们想要强制访问外层变量 a,便可以使用 global 关键字:

a = 1
def fun():
    global a # a为全局变量
    print(a) # 输出1
    a = 2 # 改变的是全局变量,因此出了这个局部作用域,仍然有效
fun()
print(a) # 输出2

注意,这个时候,不要将 global 换为 nonlocal 关键字,不然会报错:

扫描二维码关注公众号,回复: 182087 查看本文章

Syntax Error: no binding for nonlocal ‘a’ found

这是因为 nonlocal 表示外层变量,但是一旦外层变量是全局变量,则只能用 global。如果将这段代码全部放到一个函数中,则可以使用 nonlocal :

def outer_fun():
    a = 1
    def fun():
        nonlocal  a # a为外层变量
        print(a) # 输出1
        a = 2
    fun()
    print(a) #输出2
outer_fun()

同理,此时如果将 nonlocal 改为 global,也是会报错的:

builtins.NameError: name ‘a’ is not defined

注意:可以发现,之前的将 global 换为 nonlocal 的报错,和这次的将 nonlocal 改为 global 的报错,错误类型是不同的。前者是 nonlocal a 这一行报错,后者是 print(a) 这一行报错。也就是说,在使用 nonlocal a 之前,必须保证外层的确已经定义过 a 了,但是在 global a 的时候,可以允许全局变量中还没有定义过a,可以留在后面定义。比如将 print(a) 之前加上对a的定义,便不会出错:

def outer_fun():
    a = 1
    def fun():
        global  a # a为全局变量,与上面等于1的 a 没有关系
        a = 3 # 定义全局变量
        print(a) # 输出3
        a = 2
    fun()
    print(a) #输出1,局部变量
outer_fun()
print(a) # 输出2,全局变量

4、只要在一个作用域中有 global 或者 nonlocal 命令,则不管这个命令在哪个位置,在整个作用域的开始到结尾都是有效的。
如将刚才例子的 global a 和 a = 3 互换位置,结果是一模一样的:

def outer_fun():
    a = 1
    def fun():
        a = 3 # 此时python已经知道a是全局变量了,这句话相当于定义一个全局变量a,值为3
        global  a # a为全局变量
        print(a) # 输出3
        a = 2
    fun()
    print(a) #输出1,局部变量
outer_fun()
print(a) # 输出2,全局变量

如果我们注释掉 global a 这一句,那么 a = 3 就是新建了一个局部变量,其实相当于例子2了。

def outer_fun():
    a = 1
    def fun():
        a = 3
        #global  a
        print(a) # 输出3
        a = 2
    fun()
    print(a) #输出1,fun()里面的a已经释放掉了
outer_fun()
#print(a) # 这一句也要注释掉,不然会报错,因为已经没有全局变量a了

也就是说,python不允许同一个局部作用域中的同一个变量名有多种角色,即不允许在 global a 这句话之前是局部变量、之后是全局变量。所以,尽管python是解释性语言、只有在执行时才会发现类型错误,但是并不代表后面的代码不会影响到前面的代码结果。

猜你喜欢

转载自blog.csdn.net/happyrocking/article/details/80115241