闭包&关键字nonlocal和global的用法
1.闭包
一个闭包就是你调用了一个函数A,这个函数A返回了一个函数B给你。这个返回的函数B就叫做闭包。
你在调用函数A的时候传递的参数就是自由变量。
例子:
def func(name): def inner_func(age): print("name:", name, "age:", age) return inner_func bb = func("the5fire") bb(26)
这里面调用func的时候就产生了一个闭包——inner_func,即inner_func是闭包,并且该闭包持有自由变量——name,因此这也意味着,当函数func的生命周期结束之后,name这个变量依然存在,因为它被闭包引用了,所以不会被回收。
形成闭包的条件
1.外部函数内定义了内部函数
2.外部函数有返回值
3.返回的是函数名
4.内部函数引用了外部函数的变量
格式:
def 外部函数():
...
def 内部函数():
...
return 内部函数
def func(): a = 100 def inner_func(): b = 99 print(a, b) return inner_func x = func() x()
闭包传参
1 def func(a, b): 2 c = 10 3 4 def inner_func(): 5 s = a+b+c 6 print("相加后的结果:", s) 7 8 return inner_func 9 10 11 # 调用func 12 ifunc = func(1, 2) # ifunc = inner_func 13 14 # 调用ifunc 15 ifunc()
在 python 的函数内,可以直接引用外部变量,但不能改写外部变量,可以使用nonlocal和global来实现,Nonlocal 与 global 的区别在于 nonlocal 语句会去搜寻本地变量与全局变量之间的变量,其会优先寻找层级关系与闭包作用域最近的外部变量。
2.闭包的作用
闭包的最大特点是可以将父函数的变量与内部函数绑定,并返回绑定变量后的函数(也即闭包),此时即便生成闭包的环境(父函数)已经释放,闭包仍然存在,这个过程很像类(父函数)生成实例(闭包),不同的是父函数只在调用时执行,执行完毕后其环境就会释放,而类则在文件执行时创建,一般程序执行完毕后作用域才释放,因此对一些需要重用的功能且不足以定义为类的行为,使用闭包会比使用类占用更少的资源,且更轻巧灵活,现举一例:假设我们仅仅想打印出各类动物的叫声,分别以类和闭包来实现:
print("===================下面是类实现===================") class Animal: def __init__(self, animal): self.animal = animal def sound(self, voice): print(self.animal, ":", voice, "...") dog = Animal("dog") dog.sound("wangwang") dog.sound("wowo") print("===================下面是闭包实现===================") def voice(animal): def sound(voc): print(animal, ":", voc, "...") return sound dog = voice("dog") dog("wangwang") dog("wowo")
可以看到输出结果是完全一样的,但显然类的实现相对繁琐,且这里只是想输出一下动物的叫声,定义一个 Animal 类未免小题大做,而且 voice 函数在执行完毕后,其作用域就已经释放,但 Animal 类及其实例 dog 的相应属性却一直贮存在内存中:
而这种占用对于实现该功能后,则是没有必要的。
除此之外,闭包还有很多其他功能,比如用于封装等,另外,闭包有效的减少了函数参数的数目,这对并行计算非常有价值,比如可以让每台电脑负责一个函数,然后串起来,实现流水化的作业等。
1 def func(a, b): 2 c = 10 3 4 def inner_func(): 5 s = a+b+c 6 print("相加后的结果:", s) 7 8 return inner_func 9 10 11 # 调用func 12 ifunc = func(1, 2) # ifunc = inner_func 13 14 ifunc1 = func(3, 4) 15 16 ifunc1() 17 18 # 调用ifunc 19 ifunc()
每次调用func时候,inner_func都是创建一个新的内存空间,执行inner_func内的代码
因此,每次的参数值都时被保存下来的
3. 利用闭包创建计数器
1 def generate_counter(): 2 container = [0] 3 4 def add_one(): 5 container[0] = container[0] + 1 6 print("当前是第{}次访问".format(container[0])) 7 return add_one 8 9 10 counter = generate_counter() 11 counter() 12 counter() 13 counter()
关键字nonlocal和global的用法
在 python 的函数内,可以直接引用外部变量,但不能改写外部变量,可以使用nonlocal和global来实现,Nonlocal 与 global 的区别在于 nonlocal 语句会去搜寻本地变量与全局变量之间的变量,其会优先寻找层级关系与闭包作用域最近的外部变量。
1.首先,要明确 nonlocal 关键字是定义在闭包里面的(不定义在闭包里会抛异常SyntaxError: nonlocal declaration not allowed at module level)
1
2
3
4
5
6
7
8
9
10
11
12
13
|
a = 0 def func1(): a = 1 def func2(): # nonlocal a # global a a = 2 print ( 'closure a: ' , a) print (f 'func1_a: {a}' ) func2() print (f 'after func2, func1_a: {a}' ) func1() print (f 'global a: {a}' ) |
在func2中加入nonlocal: nonlocal修改闭包作用域外的变量
1
2
3
4
5
6
7
8
9
10
11
12
13
|
a = 0 def func1(): a = 1 def func2(): nonlocal a # global a a = 2 print ( 'closure a: ' , a) print (f 'func1_a: {a}' ) func2() print (f 'after func2, func1_a: {a}' ) func1() print (f 'global a: {a}' ) |
a=1不在globals作用域,因此在func2中只能用nonlocal定义后才能修改。
在func2中加入global: global修改全局作用域的变量
1
2
3
4
5
6
7
8
9
10
11
12
13
|
a = 0 def func1(): a = 1 def func2(): # nonlocal a global a a = 2 print ( 'closure a: ' , a) print (f 'func1_a: {a}' ) func2() print (f 'after func2, func1_a: {a}' ) func1() print (f 'global a: {a}' ) |
在闭包里面加入nonlocal关键字进行声明。当使用 nonlocal 时,就声明了该变量不只在嵌套函数inner()里面才有效, 而是在整个大函数里面都有效
2.global 是对整个环境下的变量起作用,而不是对函数内的变量起作用。
总结:nonlocal与global的区别
nonlocal:如果在闭包内给该变量赋值,那么修改的其实是闭包外那个作用域中的变量。
global:用来表示对该变量的赋值操作,将会直接修改模块作用域里的那个变量。(nonlocal与global互为补充)