0%

Python之变量作用域、Closure闭包

这里介绍Python变量作用域中的global关键字和nonlocal关键字、Closure闭包

abstract.png

global关键字

在内层作用域(比如,函数中),如果对全局变量进行读取/修改, 可通过global关键字来声明该变量是一个全局变量。其实如果只是读取变量不修改的话,当内层作用域找不到变量定义时,其会到外层作用域去找。需要注意的是,如果在内层作用域定义了一个与全局变量同名的局部变量,则在内层作用域操作的是局部变量,而不是全局变量

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
print("----------- 变量作用域 -----------------------")

# 全局变量
b = 1000

def f1(a):
print(f"[f1]: a -->> {a}")
# 只读不修改时,当内层作用域找不到变量b时,其会到外层作用域去找
print(f"[f1]: b -->> {b}")

def f2(a):
print(f"[f2]: a -->> {a}")
# 内层作用域定义的变量(即使其与全局变量同名)为局部变量
b = 22
print(f"[f2]: b -->> {b}")

def f3(a):
# 如果确实想在内层作用域中对全局变量进行修改, 可通过global关键字来声明变量是一个全局变量
global b
print(f"[f3]: a -->> {a}")
print(f"[f3]: b -->> {b}")
b = 500
print(f"[f3]: after modify, b -->> {b}")


print("\n--------------- Test f1 函数 ---------------------")
f1(1.1)

print("\n--------------- Test f2 函数 ---------------------")
f2(2.2)
# 全局变量b的值未变,说明f2中修改的确实是局部变量
print(f"b --->> {b}")

print("\n--------------- Test f3 函数 ---------------------")
f3(3.3)
# 全局变量b的值变了,说明f3中修改的确实是全局变量
print(f"b --->> {b}")

figure 1.png

当我们对全局变量进行修改时,如果不使用global关键字来声明该变量是一个全局变量,即会因为找不到变量定义而报错

1
2
3
4
5
6
7
8
9
num = 996

def func3():
# 对全局变量进行修改时,需要通过 global关键字来声明该变量是一个全局变量
# 否则即会因为找不到变量定义而报错
num += 50000
print(f"num -->> {num}")

func3()

figure 2.png

Closure闭包

在外层函数中嵌套定义一个内层函数时,一方面:在内层函数定义中使用了外层函数中定义的变量;另一方面,外层函数直接返回了内层函数,使得该内层函数可以在其定义环境外被执行。这样的一个内层函数我们称之为Closure闭包

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
title = "闭包定义"
print(f"------------ {title} -------------------")

def f1():
name = "Aaron"

# 这里内层函数f2使用了外层函数f1的变量name,此时就产生了闭包。其中f2就是一个闭包函数
def f2():
msg = f"{name}: Hello ..."
print(msg)

print(f"f2 函数: {f2}")
return f2

f2_new = f1()
# f2_new变量指向的就是f2函数
print(f"f2_new变量 --->>> {f2_new}")

print("函数f2中局部变量的名称:", f2_new.__code__.co_varnames)
print("函数f2中自由变量的名称:", f2_new.__code__.co_freevars)

# 从下不难看出,对于闭包函数f2而言,
# 它会保留 定义f2函数时对name变量的绑定,这样当我们在f1函数的外面调用f2函数。
# 虽然f1函数的作用域已经不存在了,但是仍能使用name变量

for cell in f2_new.__closure__:
print("函数f2中自由变量对应的值:", cell.cell_contents)

# 在f1函数外面调用f2函数,依然可以访问name变量
f2_new()

figure 3.png

nonlocal关键字

在Python中,如果内层函数需要访问外层函数的变量,可使用nonlocal关键字来进行声明

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
def make_averager():
count = 0
sum = 0

def avg(num):
# 由于在函数中变量进行修改,Python解释器会将其视为局部变量
# 为解决此问题,Python 3中引入了nonlocal关键字
# 表明其不是局部变量, 而是外层函数的变量
nonlocal count, sum
count += 1
sum += num
return sum / count

return avg

avg = make_averager()
print(f"计算平均值: {avg(20)}")
print(f"计算平均值: {avg(10)}")
print(f"计算平均值: {avg(3)}")

figure 4.png

参考文献

  1. Python编程·第3版:从入门到实践 Eric Matthes著
  2. Python基础教程·第3版 Magnus Lie Hetland著
  3. 流畅的Python·第1版 Luciano Ramalho著
请我喝杯咖啡捏~

欢迎关注我的微信公众号:青灯抽丝