函数的装饰器
闭包函数,
闭包函数 = 函数嵌套定义+ 函数对象+ 名称空间与作用域
闭: 指的是该函数时定义在一个函数内部的函数
包; 指的是该函数访问了一个来之于外层函数的变量
例:
def func(): x = 3 def func(): print(x)
这就是闭包函数, 把x 封闭到局部空间中
为函数体传参:
1. 直接使用参数的形式
def wrapper(x): print(x) wrapper(2)
2. 把函数体想要的参数包给它
def outter(x): # x = 111 def wrapper(): print(x) return wrapper # 把函数wrapper返回到全局作用域中
调用时可以在全局调用
闭包函数实现了变量定义在函数内代码只能内部访问, 二外部访问不到
装饰器
什么是装饰器
装饰器就是装饰函数的一种功能, 对原函数进行装饰
装饰器的定义, 在不改变源代码情况下, 为函数添加新功能,
函数体和调用方式都不能改变
开放封闭原则
1. 被装饰函数index
def index(x,y): print(x,y)
index(1,2)
2. 为index添加新功能 '运行时间计时'
import time def index(x,y): start = time.time() time.sleep(1) ends = time.time() print(ends-start) index(1,2)
改变了源代码, 不可行
3. def index(x,y): print(x,y) start = time.time() index(1,2) ends = time.time() print(ends-start)
没改变源代码,也没改变调用方式
但执行起来麻烦, 如果有多处需要修改, 那么就很麻烦了
4. 定义一个装饰函数, 把函数放到装饰器内
原:
def index(x,y): print(x,y) def wrapper(): start = time.time()
index(1,2) ends = time.time() print(ends-start) wrapper()
功能实现了, 但调用方式也跟着改变了, 不可行
5. 把wrapper函数内的index修改为参数形式
def index(x,y): print(x,y) def wrapper(func): start = time.time()
func(1,2) ends = time.time() print(ends-start) wrapper(index)
直接为wrapper函数传参的方式不行, 因为index是在全局, 而现在把index放到
局部中, 调用时还得先调用wrapper, 显然不符合, 不可行
6. 基于闭包函数吧wrapper函数想要传参包给index, 然后基于函数对象
把值也包给index,
def index(x,y): print(x,y) def wrapper(func): def inner(): start = time.time() func(1,2) ends = time.time() print(ends-start) return inner
index = outter(index) index()
这样大致的模仿传来传参方式了, 但如果我想把函数内的参数改变呢
还是不行, 继续改,
7. 这里使用到* 和 ** 的知识点了, 把func传参位置换成*和**
def index(x,y): print(x,y) def outter(func): def wrapper(*args,**kwargs): start = time.time() func(*args,**kwargs) ends = time.time() print(ends -start) return wrapper index(1,2)
完美保证了不该源代码和调用方式,而且还可以装饰任意函数
这样就完善了, 怎么传参都行, 但是有没有想到, 如果函数有返回值呢,
作何处理, 下面处理
8. 在前方案改进, 把wrapper函数的返回值与被装饰函数报持一致
def index(x,y): print(x,y) return 123 def wrapper(func): def inner(*args,**kwargs): start = time.time() res = func(*args,**kwargs) ends = time.time() print(ends-start) return res return inner ret = index(1,2) print(ret)
这样既可不改源代码, 又没有改变调用方式,
基本装饰器就这么实现了
标准装饰器如下
def wrapper(func): def inner(*args,**kwargs): """功能代码""" res = func(*args,**kwargs) """功能代码""" return res return inner
语糖的使用
@装饰器
@wrapper def func(): print('only')
了解:
文档注释,
把原函数的注释和文档一并拷贝到被装饰的函数中
用到functools 模块中的wraps
使用方法
在装饰器内接受函数的变量名头顶上加@wraps
例:
def wrapper(func): @wraps # 完美 def inner(*args,**kwargs): start = time.time() res = func(*args,**kwargs) ends = time.time() print(ends-start) return res return inner
叠加多个装饰器
例:
def deco1(func1): def inner(): print(func1): return inner def deco2(func2): def inner(): print(func2) return inner @deco1 @deco2 def index(x): print(x)
多个装饰器叠加的方式, 直接在函数头顶加
简单概述
当程序运行遇到语法糖@ 是, 语法糖函数会马上把@下的函数名
返回给 '@函数' 然后运行监测 '@函数' ;但不会马上运行函数体代码
结论
加载顺序从下往上
执行顺序自上往下内层的函数
2、叠加多个装饰器
2.1 加载顺序:自下而上
2.2 执行顺序:自上而下运行内层的wrapper函数
def deco1(func1): # func1 = wrapper2的内存地址 def wrapper1(*args,**kwargs): print('wrapper1====>') res1=func1(*args,**kwargs) return res1 return wrapper1 def deco2(func2): # func2 = wrapper3的内存地址 def wrapper2(*args,**kwargs): print('wrapper2====>') res2=func2(*args,**kwargs) return res2 return wrapper2 def deco3(func3): # func3 = 最原始的那个被装饰函数的内存地址 def wrapper3(*args,**kwargs): print('wrapper3====>') res3=func3(*args,**kwargs) return res3 return wrapper3 # index=wrapper1的内存地址 @deco1 # deco1(wrapper2的内存地址)=>wrapper1的内存地址 @deco2 # deco2(wrapper3的内存地址)=>wrapper2的内存地址 @deco3 # deco3(最原始的那个被装饰函数的内存地址)=>wrapper3的内存地址 def index(x,y): print('index=>',x,y) index(1,2)
结果
""" wrapper1====>' wrapper2====> wrapper3====> index=>1,2 """