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