一、完美的装饰器(纠正装饰器的函数指向)
1、装饰器的前后执行的解释
def wrapper(f): def inner(*args,**kwargs): #定义函数的时候,属于接收参数,此时 *args,**kwargs聚合为元组,字典 print('装饰在函数前的') print(args) #args参数本身,依旧是 元组 print(*args) ret = f(*args,**kwargs) #调用函数的时候,属于使用参数,此时 *args,**kwargs拆分为元组的各元素,字典的各key-value print('装饰在函数后的') return ret return inner @wrapper #fun = wrapper(fun) #装饰器只是一个传话的媒介 def fun(a,b): print('fun开始了') print('a',a) print('b',b) return 'fun返回值' re = fun('wangjing',1234) print(re)
2、此处添加2个知识点
def wahaha(): ''' 一个打印娃哈哈的函数 :return: ''' print('娃哈哈') print(wahaha.__name__) #函数的名称的字符串格式 print(wahaha.__doc__) #函数的注释
import os print(os.path.getsize('web_cache') ) #获取文件大小
3、查看基本的装饰器对 原函数产生的变化
注意结果中的 fun.__name__ 的值已经变成了inner!!!!也就是说虽然添加了装饰器wrapper的功能,但是fun函数已经不存在,而是变成了inner
def wrapper(f): def inner(*args,**kwargs): #定义函数的时候,属于接收参数,此时 *args,**kwargs聚合为元组,字典 ''' 这是inner自己的注释 :param args: :param kwargs: :return: ''' print('装饰在函数前的') print(args) #args参数本身,依旧是 元组 print(*args) ret = f(*args,**kwargs) #调用函数的时候,属于使用参数,此时 *args,**kwargs拆分为元组的各元素,字典的各key-value print('装饰在函数后的') return ret return inner @wrapper #fun = wrapper(fun) #装饰器只是一个传话的媒介 def fun(a,b): ''' 这是fun原来的的注释 :param a: :param b: :return: ''' print('fun开始了') print('a',a) print('b',b) return 'fun返回值' re = fun('wangjing',1234) print(re) print(fun.__name__) #实际上fun的函数名已经变成了inner,因为在全局名字空间中没有fun的函数名,fun作为wrapper的参数存储
4、添加一个第三方的装饰器,使fun函数完全不变化,依旧是他本身。即在inner函数上添加 一个第三方的带参数的装饰器
from functools import wraps def wrapper(f): @wraps(f) #是一个第三方的装饰器,作用是 完全不改变fun的本质,但是依旧可以实现装饰器wrapper的功能 def inner(*args,**kwargs): #定义函数的时候,属于接收参数,此时 *args,**kwargs聚合为元组,字典 ''' 这是inner自己的注释 :param args: :param kwargs: :return: ''' print('装饰在函数前的') print(args) #args参数本身,依旧是 元组 print(*args) ret = f(*args,**kwargs) #调用函数的时候,属于使用参数,此时 *args,**kwargs拆分为元组的各元素,字典的各key-value print('装饰在函数后的') return ret return inner @wrapper #fun = wrapper(fun) #装饰器只是一个传话的媒介 def fun(a,b): ''' 这是fun原来的的注释 :param a: :param b: :return: ''' print('fun开始了') print('a',a) print('b',b) return 'fun返回值' re = fun('wangjing',1234) print(re) print(fun.__name__) #此时fun的函数名依旧是本身的fun
二、带参数的装饰器
给个场景:给函数计算执行时间,之后可能又把这些装饰作用在全部函数中删除
1、先写一个函数:不包含集体删除的功能,写一个装饰器可计算函数执行时间
import time def timer(func): def inner(*args,**keargs): start = time.time() ret = func(*args,**keargs) end = time.time() print(end - start) return ret return inner @timer def wahaha(): time.sleep(0.1) print('wahaha') @timer def shuangww(): time.sleep(0.2) print('shuangww') wahaha() shuangww()
2、再写一个函数:包含集体删除的功能,写一个装饰器可计算函数执行时间,并且可以通过一个布尔变量的值 控制是否进行删除的功能
import time flag = False def timer_out(flag): def timer(func): def inner(*args,**keargs): if flag: start = time.time() ret = func(*args,**keargs) end = time.time() print(end - start) return ret else: ret = func(*args,**keargs) return ret return inner return timer @timer_out(flag) #装饰器的 函数名=括号,则代表函数执行,先执行函数(timer = timer_out(flag))再进行装饰器操作(实际上等于 @timer,即wahaha = timer(wahaha)) def wahaha(): time.sleep(0.1) print('wahaha') @timer_out(flag) def shuangww(): time.sleep(0.2) print('shuangww') wahaha() shuangww()
3、最后凑数总结一下逻辑走向
在1中的装饰器的外层再加一个函数,该函数有一个 参数flag,返回值为 timer函数~~
装饰器的装饰方式改为:@timer_out(flag)
装饰器的 函数名=括号,则代表函数执行,先执行函数(timer = timer_out(flag))再进行装饰器操作(实际上等于 @timer,即wahaha = timer(wahaha))
三、多个装饰器装饰一个函数
def wrapper1(func): #func-->innner2 def inner1(): print('inner1:before func') func() #f print('inner1:after func') return inner1 def wrapper2(func): #func-->f def inner2(): print('inner2:before func') func() print('inner2:after func') return inner2 @wrapper1 @wrapper2 def f(): print('f被执行了') f()
最后凑一下逻辑走向:
1、把wrapper1放到内存里
2、把wrapper2放到内存里
3、处理装饰器,按照从上到下的执行原则,走到@wrapper1,但是装饰器必须得找到距离最近的函数才能真正发生作用,因此走到了@wrapper2,即
f = wrapper2(f)== inner2
此时wrapper1的形参func指向了f
4、之后由于最近的函数已经找到了,所以再执行@wrapper1,此时f已经指向inner2了,此时所以
#f = wrapper1(f)-->wrapper1(inner2)== inner1
此时wrapper2的形参func指向了inner2
5、此时走到了f(),即调用f函数,此时f函数已经指向了inner1,最终的执行是 inner1,注意执行的inner1中的func函数指的是 inner2,所以最终执行结果是:
inner1:before func
inner2:before func
f被执行了
inner2:after func
inner1:after func
注意:装饰器的部分就只是各自指来指去,只有到f()才有真实的执行,才有了打印结果,也就是说最终只执行了一个部分:inner1,其中inner1使用了一个 上层局部变量:func,对应func指的是 inner2。三层的装饰器就类比即可,所以只记现象就可以了
使用场景:比如想要写两个装饰器,1记录用户登录情况,2计算登录函数执行时间,这个时候2一定作为前一个装饰器
再以上基础上把装饰器的返回值加上,结果返回值用的是 f的哟“hahaha”
def wrapper1(func): #func-->innner2 def inner1(): print('inner1:before func') ret = func() #f print('inner1:after func') return ret return inner1 def wrapper2(func): #func-->f def inner2(): print('inner2:before func') ret = func() print('inner2:after func') return ret return inner2 @wrapper1 @wrapper2 def f(): print('f被执行了') return 'hahahha' print(f())