闭包:
1. 闭包是嵌套在函数中的函数。
2. 闭包必须是内层函数对外层函数的变量(非全局变量)的引用。
例如:
上面被红色方框框起来的区域就是闭包,变量a是wrapper()函数的局部变量,它应该是随着wrapper()函数的执行结束之后而消失。但是他没有,是因为此区域形成了闭包,a变量就变成了一个叫自由变量的东西,inner函数的作用域会延伸到包含自由变量a的绑定。也就是说,每次我调用ret对应的inner函数 时,都可以引用到这个自用变量a,这个就是闭包。
我们可以通过一些方法来查看自由变量:
# 函数名.__code__.co_freevars 查看函数的自由变量
# 函数名.__code__.co_varnames 查看函数的局部变量
# 函数名.__closure__ 获取具体的自由变量对象,也就是cell对象。
# cell_contents 自由变量具体的值
闭包的作用:保存局部信息不被销毁,保证数据的安全性。
装饰器的目的是什么 ?简单点讲就是达到“开放封闭”,即对扩展是开放的,对源码是封闭。
看下面这个例子:
import time
def index():
time.sleep(2) # 模拟一下网络延迟以及代码的效率
print('欢迎访问博客园主页')
# return '访问成功'
def timer(func): # func = index
def inner():
start_time = time.time()
func()
end_time = time.time()
print(f'此函数的执行时间为{end_time - start_time}')
return inner
index = timer(index)
print(index())
#输出:
欢迎访问博客园主页
此函数的执行时间为2.0024185180664062
None
我们先分析一下执行流程:
看清执行流程后我们就能明白为什么最后print打印 的是None, 因为inner函数没有返回值,所以默认就是None.
如果要改变这一情况,那么我们就需要在inner内部将fun()的返回值返回,而且index函数也得提供返回值,而不是默认的None.
加上传参数的功能后:
def home(name, age):
time.sleep(3) # 模拟一下网络延迟以及代码的效率
print(name, age)
print(f'欢迎访问{name}主页')
def timer(func): # func = home
def inner(*args, **kwargs):
start_time = time.time()
func(*args, **kwargs)
end_time = time.time()
print(f'此函数的执行时间为{end_time - start_time}')
return inner
# @timer
home = timer(home)
home('andy', 18)
#输出:
andy 18
欢迎访问andy主页
此函数的执行效率为3.0016109943389893
模板:
from functools import wraps
def wrapper(func):
@wraps(func)
def inner(*args,**kwargs):
'''执行被装饰函数之前的操作'''
ret = func
'''执行被装饰函数之后的操作'''
return ret
return inner
带参数的装饰器:
import time
from functools import wraps
def outer(arg):
def timer(func): # func = home
@wraps(func)
def inner(*args, **kwargs):
if arg < 18:
print('未成年')
else:
print(f'{arg} 已经成年了')
start_time = time.time()
res = func(*args, **kwargs)
end_time = time.time()
print(f'此函数的执行时间为{end_time - start_time}')
return res
return inner
return timer
@outer(19)
def home(name, age):
time.sleep(3) # 模拟一下网络延迟以及代码的效率
print(name, age)
print(f'欢迎访问{name}主页')
home('andy', 19)
@outer(19) :分两步:
第一步先执行outer(19)函数,得到返回值timer
第二步@与timer结合,形成装饰器@timer 然后在依次执行。
这里通过装饰器传递了一个额外的参数,而因为有这个参数我们就可以做更多的判断,或者其它操作,即扩展更多的功能。