0%

Python的global和nonlocal关键字

今天在查阅Python的官方文档的时候,注意到了global和nonlocal这两个关键字,它们都和函数的作用域有关联,且效果比较类似,都可以修改函数定义域之外的特定值,但是两者之间也有着一定区别。

结合官方文档以及自己的代码实践,我对它们的作用进行了一些总结。

Global关键字修饰的是整个代码块中的全局变量。即加上global关键字后,本来只能在某一代码块(比如函数)作用域里起效果的局部变量,就升级成在整个代码中都可以读取的全局变量了。

如果说在代码最外层已经有该变量的定义(不论是否加上global关键字),或者在其它代码块里已经有该全局变量的定义,那么在代码块里面对同名变量加上global关键字后,如果修改代码块里面该变量的值,那么所有同名的全局变量都会发生改变。

1
2
3
4
5
6
7
8
9
10
11
12
def global_modify_name():
global name
name = 'lisi'

def non_global_modify_name():
name = 'wangwu'

name = 'zhangsan'
non_global_modify_name()
print(name)
global_modify_name()
print(name)

以上代码的输出如下:

1
2
zhangsan  
lisi

可以看出,如果没有加global关键字的话,由于作用域的限制,在函数体里面的修改是不会影响到全局变量的。(第一行的输出依旧是’zhangsan’而不是’wangwu’)而加上global关键字之后,在函数体里的修改就相当于直接在外部对全局变量进行修改了,所以第二行的输出即变为’lisi’。

除此之外,Global关键字定义的变量无法直接进行赋值的,需要先定义然后再进行赋值。

1
2
3
global x = 1 # 这样会报错  
global x
x = 1 # 需要这么定义

另外,如果一个变量在声明为global之前,并没有相关的定义。那么如果在声明为global之前调用该变量,会出现调用错误,这和没声明一个普通的变量就调用它是一样的。即global产生的效果是在声明其为global后。

比如,我们在non_global_modify_name里加上一个global变量test的定义。(global关键字可以在任意位置出现,因此也可以在函数体内定义全局变量)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
def global_modify_name():
global name
name = 'lisi'

def non_global_modify_name():
name = 'wangwu'
global test
test = 'this is a test'

name = 'zhangsan'
# print(test) # 变量还没声明,调用会出错
non_global_modify_name()
print(name)
print(test)
global_modify_name()
print(name)

如果我们把注释掉的那行去掉,那么会因为变量此时还没声明而报错。

1
NameError: name 'test' is not defined

如果将这行加上注释之后,可以看到最后输出了对应的全局变量test。

1
2
3
zhangsan
this is a test
lisi

nonlocal是python3.x中新增的关键字。在变量的定义加上nonlocal关键字之后,这个变量会指向最近包含的代码块作用域中除全局变量之外的相同名字的变量。在代码块里对nonlocal变量进行修改,等同于对最近包含代码块中对应同名非全局变量的修改。

如果找不到作用域外同名的非全局变量定义的话,则该定义会报错。同时由于代码最外层的变量无论是否加global都算是全局变量,所以nonlocal的定义无法在单层函数及最外层代码块里面出现。

1
2
3
4
5
6
7
8
9
10
11
12
def nonlocal_modify_name():
nonlocal name = "nonlocal" # 报错
def c():
nonlocal abc # 找不到(no binding),报错
nonlocal name # 这样可以
name = "nonlocal2"
#nonlocal name
c()
print("nonlocal", name)


nonlocal name = 'zhangsan' # 报错

比如说以上这一代码,nonlocal无法在最外部以及第一层函数里面出现。但是可以在第二层函数里面进行相关的定义。对其进行修改之后的代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
def nonlocal_modify_name():
name = "this is local"
def c():
nonlocal name
name = "nonlocal2"
c()
print("nonlocal -> ", name)


name = 'zhangsan'
non_global_modify_name()
print(name)
nonlocal_modify_name()
print(name)

可以看到结果如下,

1
2
3
zhangsan
nonlocal -> nonlocal2
zhangsan

执行子函数c()之后,nonlocal关键字定义的变量name发生了修改,与此同时外面一层函数内的变量name也发生了改变,不过这个变化仅局限于函数nonlocal_modify_name()内,并没有对全局变量造成任何影响。(全局变量的name仍然是’zhangsan’)

由于nonlocal的话是找寻与它最近一层的相同名字的变量,所以只要能找到对应的变量,就可以对其进行修改。比如稍微修改一下之前的栗子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
def nonlocal_modify_name():
name = "this is local"
def c():
def d():
def e():
def f():
nonlocal name
name = "nonlocal2"
f()
e()
d()
c()
print("nonlocal -> ", name)


name = 'zhangsan'
non_global_modify_name()
print(name)
nonlocal_modify_name()
print(name)

可以看到即使nonlocal定义的变量经过了多层的嵌套,但是只要它能找到外层对应的变量,依旧可以修改它的值。最后的结果和之前是一样的。

1
2
3
zhangsan
nonlocal -> nonlocal2
zhangsan

在对这两个关键字进行一些总结之后,可以看出它们之间的最大两个区别如下:

  1. global关键字可以存在于任何位置,但nonlocal关键字只能存在于嵌套函数之内
  2. nonlocal关键字定义的变量依赖于外层函数的同名非全局变量(没有则会报错),而global关键字并没有这一限制(因为只要这么定义得到的变量都是全局变量,不论声明是在哪一层,它们等级是一样的)