闭包

浏览 769

课文

在 python 里万物皆对象,函数也不例外,于是乎我们可以在一个函数中定义另一个函数作为返回值。 ```python def func(): def wrapper(): print('你好') return wrapper result= func() result() ``` ```output 你好 ``` 这就是一个闭包(closure)了吗?还少了点味。 因为根据维基百科上对闭包的定义: > 在支持头等函数的语言中,如果函数f内定义了函数g,那么如果g存在自由变量,且这些自由变量没有在编译过程中被优化掉,那么将产生闭包。 人话: f 函数中定义了 g 函数, g 还引用了 f 定义的变量, 就是闭包。 代码: ```python def func(name): def wrapper(): print(f'你好,{name}') return wrapper result = func('小明') result() ``` 由作用域的查找规则可知,在这段代码中,`func` 函数内是局部作用域, `wrapper` 函数内是闭合作用域, wrapper 函数可以直接使用 func 函数内的变量。 那 python 是怎么实现闭包的呢? 在实现闭包时,返回的函数会带有 __closure __ 属性,在__closure __ 里可以找到函数所需要的变量。 ```python print(result.__closure__) # 打印一下 __closure__ 属性 print(result.__closure__[0].cell_contents) # 通过 cell_contents 可以得到保存的变量 ``` ```output (<cell at 0x000001DF6F6B86D0: str object at 0x000001DF6F6B5C30>,) 小明 ``` ## 闭包的应用场景 使用闭包主要有以下三个好处。 - 关联数据与函数 - 保存临时变量,减少全局变量 - 保存私有变量,防止变量被修改 *在 python 中闭包有个很经典的应用场景就是装饰器,放在下节讲。* ### 关联数据与函数 关联数据与函数,这与面向对象中很类似,但有时只有一个方法时,使用闭包会简单很多。 ```python # 写法 1:用类实现 class Timer: def __init__(self, base): self.base = base def time(self, x): return self.base * x timer2 = Timer(2) print('结果:', timer2.time(4)) # 写法 2:用闭包实现 def timer(base): def wrapper(x): return x * base return wrapper timer2 = timer(2) print('结果:', timer2(4)) ``` ```output 结果: 8 结果: 8 ``` 以上两种方式,用闭包实现会简单很多。 ### 保存临时变量 由于闭包是把变量保存在局部作用域中,所以可以减少使用全局变量。 ```python # 写法 1:将变量定义在全局作用域 count = 0 def func(): global count count += 1 print(f'执行了{count}次') func() func() # 写法 2:将变量定义在闭包中的局部作用域 def counter(): count = 0 def wrapper(): nonlocal count count += 1 print(f'执行了{count}次') return wrapper func = counter() func() func() ``` ```output 执行了1次 执行了2次 执行了1次 执行了2次 ``` ### 保存私有变量 闭包中传入的变量只能被内部的函数修改,这使得我们可能通过这个特性创建一个防止被修改的字典。 ```python def my_dict(**kwargs): def wrapper(): # 这里是重新生成了一个字典对象, # 因为如果直接返回则属于浅拷贝, # 会导致外部仍能修改字典的值。 return {**kwargs} return wrapper xiaoming = my_dict(name='小明', age=18) print('修改前:', xiaoming()) xiaoming()['name'] = '小红' print('修改后:', xiaoming()) ``` ```output 修改前: {'name': '小明', 'age': 18} 修改后: {'name': '小明', 'age': 18} ``` 成功避免了被更名改姓。

评论

登录参与讨论

暂无评论

共 0 条
  • 1
前往
  • 1

课程目录

第一课:命名空间与作用域 第二课:闭包 第三课:装饰器 第四课:迭代器 第五课:生成器

学习遇到困难?微信扫码进入社群与小伙伴一起交流讨论。