闭包&关键字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
= 0
def func1():
    = 1
    def func2():
        # nonlocal a
        # global 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
= 0
def func1():
    = 1
    def func2():
        nonlocal a
        # global 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
= 0
def func1():
    = 1
    def func2():
        # nonlocal a
        global 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互为补充)

 

posted @ 2020-02-06 23:12  GumpYan  阅读(409)  评论(0编辑  收藏  举报