什么是装饰器 让其他函数在不需要做任何代码变动的前提下,增加额外的功能,装饰器的返回值也是一个函数对象。 装饰器的应用场景:比如插入日志,性能测试,事务处理,缓存等等场景。
简单来说,就是将一个定义好的函数,将其函数名传入另外一个函数,在另一个函数中加入其他的功能,最后返回出新的函数的名,让原本的函数名去接收,完成这个原本函数的更新
案例 1 2 3 4 5 6 def func1 (): print ("in func1" )
解决方案
1 2 3 4 5 6 7 8 def func2 (func ): def inner (): print ("hello world" ) func() print ("hello python" ) return inner func1 = func2(func1) func1()
装饰器的形成过程 如果我想测试某个函数的执行时间
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 import timedef func1 (): print ('in func1' ) def timer (func ): def inner (): start = time.time() func() time.sleep(1 ) print (time.time() - start) return inner func1 = timer(func1) func1()
但是如果有多个函数,我都想让你测试他们的执行时间,你每次是不是都得func1 = timer(func1)?这样 还是有点麻烦,因为这些函数的函数名可能是不相同,有func1,func2,graph,等等,所以更简单的方 法,python给你提供了,那就是语法糖。
语法糖的用法是,先定义一个修饰器,例如像上一个算时间的修饰器,搞个语法糖的叫做@timer
将这个语法糖黏在定义的新函数的上方,即可用timer这个修饰器去修饰这个新定义的函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 import timedef timer (func ): def inner (): start = time.time() func() print (time.time() - start) return inner @timer def func1 (): time.sleep(1 ) print ('in func1' ) func1()
装饰一个带各种参数的函数 1 2 3 4 5 6 7 8 9 10 11 12 13 14 import timedef timer (func ): def inner (*args,**kwargs ): start = time.time() func(*args,**kwargs) print (time.time() - start) return inner @timer def func1 (*args,**kwargs ): print (args,kwargs) func1('hello world' ,'abc' ,123 ,432 ,name='zhangsan' )
与原本的装饰方式类似,但是函数需要将变量的类型进行修改
wraps装饰器 查看函数的相关信息 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 def index (): '''这是一条注释信息''' print ('from index' ) print (index.__doc__) print (index.__name__) 加上装饰器def outer (func ): def inner (): '''这里是inner''' func() return inner @outer def index (): '''这是一条注释信息''' print ('from index' ) print (index.__doc__) print (index.__name__) 这是一条注释信息 index 这里是inner inner
导入wraps修饰器,可以保留函数本身的属性以及相关的注释
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 from functools import wrapsdef outer (func ): @wraps(func ) def inner (): '''这里是inner''' func() return inner @outer def index (): '''这是一条注释信息''' print ('from index' )print (index.__doc__) print (index.__name__) from functools import wrapsdef outer (func ): @wraps(func ) def inner (*args,**kwargs ): '''这里是inner''' func(*args,**kwargs) return inner@outer def index (): '''这是一条注释信息''' print ('from index' )print (index.__doc__) print (index.__name__)
wraps修饰器就是在正常的修饰器种加入一个@wraps(形参),即可保留函数原本的信息
带控制参数的装饰器 加上一个outer函数,可以携带一个flag的值,然后控制装饰器是否生效
解释:在修饰糖的后面加入一个变量或者布尔值,在修饰器的逻辑种加入一个判断,如果为True则进行修饰,如果为False则不进行修饰,但是,不管有没有修饰,修饰器已经起作用,所以为了函数的信息不发生变化,必须让wraps修饰器起作用,判断函数归判断函数,必须让wraps修饰器起作用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 from functools import wrapsdef oouter (flag ): def outer (func ): @wraps(func ) def inner (*args, **kwargs ): '''这里是inner''' if flag: print ("开始装饰" ) func(*args, **kwargs) print ("装饰结束" ) else : func(*args, **kwargs) return inner return outer@oouter(True ) def index (a ): '''这是一条注释信息''' print ('from index' ) print (a) print (index.__doc__) print (index.__name__) index('abc' )
多个装饰器装饰一个函数 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 def wrapper1 (func ): def inner (): print ('第一个装饰器,在程序运行之前' ) func() print ('第一个装饰器,在程序运行之后' ) return inner def wrapper2 (func ): def inner (): print ('第二个装饰器,在程序运行之前' ) func() print ('第二个装饰器,在程序运行之后' ) return inner @wrapper1 @wrapper2 def f (): print ('Hello' ) f()
总结:哪个修饰糖距离更近就先执行哪个,在执行完一个后,这个函数已经发生了变化,将由修饰糖2修饰过的函数交给修饰糖1进行再次修饰,从而得出修饰结果
开放封闭原则 软件实体应该是可扩展但是不可修改的。
装饰器完美的遵循了这个开放封闭原则
装饰器的主要功能和固定结构 本科所学习的知识总结运用 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 def outer (func ): def inner (*args,**kwargs ): '''执行函数之前要做的''' re = func(*args,**kwargs) '''执行函数之后要做的''' return re return inner from functools import wrapsdef outer (func ): @wraps(func ) def inner (*args,**kwargs ) '''执行函数之前要做的''' re = func(*args,**kwargs) '''执行函数之后要做的''' return re return inner from functools import wrapsdef oouter (flag ): def outer (func ): @wraps(func ) def inner (*args,**kwargs ) if flag: '''执行函数之前要做的''' re = func(*args,**kwargs) '''执行函数之后要做的''' else : re = func(*args,**kwargs) return re return inner