闭包函数和装饰器
![]()
闭包函数(重要)
# 闭包函数的两大特性
1. 闭:定义在函数内部的函数
2. 包:内部函数使用了外部函数名称空间中的名字
eg:
def outer():
x = 1
def inner(): # 定义在函数内部的函数
print(x) # 调用了外部函数名称空间的变量
return inner # 外部函数返回内部函数名
res = outer()
res()
运行结果:
1
闭包函数实际应用
# 闭包函数是给函数体传参的另外一种方式
# 函数体传参方式1:形参传值
# def func(username):
# print(username)
# func('petter') # 把函数体需要的形参通过实参传过去
# 函数体传参方式2:闭包
eg:
def outer(x):
# x = 1
def inner():
print(x) # 直接调用外部函数名称空间的绑定的形参
return inner
res = outer(1) # 给外层函数传参
res()
运行结果:
1
![]()
装饰器简介
# 装饰器的本质
在不改变被装饰对象原有的'调用方式'和'内部代码'的情况下给被装饰对象添加新功能
# 装饰器的原则
对拓展开放
对修改封闭
# 需求:统一函数的执行时间
import time
def index():
time.sleep(3)
print('from index')
'''给index函数增加了一个统计执行时间的功能'''
start_time = time.time() # 函数执行之前获取一个时间戳
index()
end_time = time.time() # 函数执行之后获取一个时间戳
print(end_time - start_time) # 两个时间戳的差值就是函数的执行时间
输出结果:
from index
3.004551887512207
简易版本装饰器
# 装饰器实现上述需求
eg:
def outer(func_name): # 1. 定义外部函数
def inner(): # 3.定义内部函数
start_time = time.time() # 7. 获取函数运行前的时间戳
func_name() # 8. 调用函数,内部函数调用外部函数绑定的形参,由于第2步形参传值,func_name与index临时绑定到一起且这里是先调用outer函数再赋值,所以func_name绑定的是上面的函数名index,即调用函数index()
end_time = time.time() # 9. 获取函数运行后的时间戳
print(end_time - start_time) # 输出函数运行前后时间戳的差值
return inner # 4. 把内部函数的函数名返回出去
index = outer(index) # 2. 调用外部函数 # 5. 定义变量名index,让index指向outer的返回值
index() # 6. 调用函数,由于index指向inner,所以index()等价于inner(),即调用函数inner()
运行结果:
from index
3.0023038387298584
![]()
完整版装饰器
上述简易版装饰器还有两个点有问题,我们回想函数的语法结构,可以看到,上述装饰器在index函数有参数传递和返回值时无法实现,完整版的装饰器其实就是解决了这两个问题
eg:
import time
def index(username):
time.sleep(3)
return username
# 简易版装饰器
def outer(func_name):
def inner(*args, **kwargs): # 定义可变长形参,无论什么值传过来都可以接收,无论原函数有无参数都不会报错
start_time = time.time()
res = func_name(*args, **kwargs) # 这里也要定义可变长形参,这里还要搞个变量接收原函数的返回值
end_time = time.time()
print(end_time - start_time)
return res # 这里把原函数的返回值返回出去,原函数没有返回值返回的是None,这样无论原函数有无返回值都不会报错
return inner
index = outer(index)
res = index('这里参数随便传,反正不报错')
print(res)
运行结果:
3.0157086849212646
这里参数随便传,反正不报错
![]()
装饰器模板(重要)
其实根据上面的代码,把原函数不存在的功能全删了就是模板
def outer(func_name): # func_name用于接收被装饰的对象(函数)
def inner(*args, **kwargs):
'''原函数被执行前的操作写这里'''
res = func_name(*args, **kwargs) # 执行被装饰对象
'''原函数被执行后的操作写这里'''
return res # 返回被装饰对象的返回值
return inner # 返回内部函数名,供全局调用
装饰器语法糖
# 作用仅仅是让代码编写的更加好看、简洁
eg:
'''上接完整版装饰器'''
@outer # 相当于执行了 my_age = outer(my_age)
def my_age(age):
time.sleep(3)
return age
res = my_age(18)
print(res)
运行结果:
3.0101282596588135
18
"""
语法糖内部原理
1.使用的时候最好紧跟在被装饰对象的上方
2.语法糖会自动将下面紧挨着的函数名传给@后面的函数调用
"""
装饰器修复技术
eg:
from functools import wraps
def outer(func_name):
@wraps(func_name)
def inner(*args, **kwargs):
print('下面这货被装饰了')
res = func_name(*args, **kwargs)
return res
return inner
@outer
def index():
'''这是真函数的注释'''
print('from index')
index()
print(index)
help(index)
运行结果:
下面这货被装饰了
from index
<function index at 0x01592030>
Help on function index in module __main__:
index()
这是真函数的注释
'''wraps模块warps关键字可以把真函数的属性提取出来,赋值给装饰器,达到以假乱真的地步'''
![]()