Python: 关键字global和nonlocal

学习关键字 globalnonlocal 的用法。

引例

先来看一个代码片段:

1
2
3
4
5
6
7
8
9
10
11
12
13
increment = lambda x: x + 1

def make_repeater(func, n):
"""Return the function that computes the nth application of func."""
def repeater(x):
while n > 0:
x = func(x)
n = n - 1
return x
return repeater

add_three = make_repeater(increment, 3)
add_three(5)

当运行上述代码片段时,会产生如下的错误:

1
UnboundLocalError: local variable 'n' referenced before assignment

要理解这个错误的原因,需要知道全局变量和局部变量的概念。

局部变量和全局变量

局部变量

当我们在函数体内、或者在局部范围内定义一个变量时,该变量被称为 局部变量 。局部变量只能在局部范围内被访问,不能在局部范围外被访问。

例1:在范围外访问局部变量

1
2
3
4
5
6
7
def foo():
y = "local"
print(y)


foo()
print(y)

输出:

1
2
local
NameError: name 'y' is not defined

全局变量

当我们在函数外、或者在全局范围内定义一个变量时,该变量被称为 全局变量 。全局变量可以在函数内部被访问,也可以在函数外部被访问。

让我们来看一个全局变量的例子:

例3:全局变量

1
2
3
4
5
6
7
8
x = "global"

def foo():
print("x inside:", x)


foo()
print("x outside:", x)

输出:

1
2
x inside: global
x outside: global

在上述代码中,x 是一个全局变量,我们可以在函数 foo 内部访问 x ,也可以在函数外部访问 x

值得注意的是,在上面的代码中,我们仅仅只是访问 x 。但如果我们想要做的是修改 x 的值呢?

例4: 在函数内部修改全局变量

1
2
3
4
5
6
7
x = "global"

def foo():
x = x * 2
print(x)

foo()

Output

1
UnboundLocalError: local variable 'x' referenced before assignment

运行会出现错误。

这是因为,当我们在函数内部对 x 修改重新赋值时,Python解释器会认为 x 是局部变量而不是全局变量,而这个局部变量也并没有在函数内部定义,所以就产生了这个错误。

关键字:global和nonlocal

global关键字

为了解决例4的问题,我们使用 global 关键字。现在函数内部声明变量 x 是全局变量,再对其进行修改。代码如下:

1
2
3
4
5
6
7
8
9
x = "global"

def foo():
global x
x = x * 2
print(x)

foo()
print(x)

输出:

1
2
globalglobal
globalglobal

nonlocal关键字

比较以下两个代码:

例5:不使用nonlocal关键字

1
2
3
4
5
6
7
8
9
def func1():
name = 'variable 1'
def foo():
name = 'variable 2'
print(name)
foo()
print(name)

func1()

输出:

1
2
variable 2
variable 1

例6:使用nonlocal关键字

1
2
3
4
5
6
7
8
9
10
def func2():
name = 'variable 1'
def foo():
nonlocal name
name = 'variable 2'
print(name)
foo()
print(name)

func2()

输出:

1
2
variable 2
variable 2

注意到例6的第4行代码,嵌套函数 foo 中的变量 name 使用了关键字 nonlocal

如果一个变量被 nonlocal 修饰,则标识了该变量是上一级函数中的局部变量,在这里,指的是函数 func2 中的同名的变量 name (line 2) 。因此在嵌套函数 foo 中修改变量 name 的值也就修改了 func2 中变量 name 的值。

而在例5中,嵌套函数 foo 中的变量 name 并没有用 nonlocal 修饰,那么它仅仅是 foo 中的一个局部变量。在函数 foo 的范围外是不能访问这个变量的。所以第7行打印的是第2行定义的局部变量 name, 而不是第4行的局部变量。

尾声

最后让我们重新看一下引例中的代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
increment = lambda x: x + 1

def make_repeater(func, n):
"""Return the function that computes the nth application of func."""
def repeater(x):
while n > 0:
x = func(x)
n = n - 1
return x
return repeater

add_three = make_repeater(increment, 3)
add_three(5)

要怎样修改才能使代码工作呢?

答案不唯一,但一个可行的办法是使用 nonlocal 关键字来修饰变量 n , 如下所示:

1
2
3
4
5
6
7
8
def make_repeater(func, n):
def repeater(x):
nonlocal n
while n > 0:
x = func(x)
n = n - 1
return x
return repeater

🌼 完结,撒花 ✿✿ヽ(°▽°)ノ✿