python----装饰器函数
装饰器函数
1.装饰器的形成过程
当我们作为python开发,写好的一些代码(函数)会给其他同事调用,但是当有新的需求,并且在没有改原有代码(函数)的前提实现新的新的需求,这时我们该怎么办呢?
下面我们举个简单的例子来引入装饰器
1 def func(): 2 time.sleep(0.01) 3 print("hello,大家好")
上面已经写好了一个func的函数,现在我们需要统计一下这个函数的执行时间:
我们可以直接改代码来实现:
1 import time 2 3 def func(): 4 start_time=time.time() 5 time.sleep(0.01) 6 print("hello,大家好") 7 stop_time=time.time() 8 print('执行时间是 %s'%(stop_time-start_time)) 9 10 func()
通过直接改函数内的代码可以实现统计这个函数的执行时间的需求,但如果现在有10000个函数都需要统计执行时间,那就得一个一个改一遍,后续这个需求不需要了,又要一个个的删除去之前加的代码,这样也太麻烦了,那我们怎么办呢?
我们可以写出一个的统计时间的函数timmer()
import time def timmer(func): start_time=time.time() func() stop_time=time.time() print('执行时间是 %s'%(stop_time-start_time)) def func(): time.sleep(0.01) print ("hello,大家好") timmer(func)
这样就比之前简单多了,不管有多少函数修改,都不用直接修改代码,直接调用这个timmer()函数来实现,但是这样还是改变了原来函数的调用方法,原来直接执行func(),但是现在变成了timmer(func),这样也需要改N多次才能实现,还是很麻烦。
如果能不改变函数的调用方法,直接执行func(),就能实现调用timmer()方法的效果,那就完美了
import time def timmer(func): start_time=time.time() func() stop_time=time.time() print('执行时间是 %s'%(stop_time-start_time)) def func(): time.sleep(0.01) print ("hello,大家好") func=timmer #为了不改变之前的调用方法,做了这样的修改 func() #这样报错,无法执行#
试着修改,可惜代码报错,因为timer方法需要传递一个func参数,我们不能在赋值的时候传参,因为只要执行func = timmer(func),timmer方法就直接执行了,下面的那句func根本就没有意义。
这时我们就需要装饰器来完成。
装饰器的形成
import time def timmer(func): def inner(): start_time=time.time() func() stop_time=time.time() print('执行时间是 %s'%(stop_time-start_time)) return inner def func(): time.sleep(0.01) print ("hello,大家好") func=timmer(func) func()
忙活了这么半天,终于初具规模了!现在已经基本上完美了,唯一碍眼的那句话就是还要再作一次赋值调用。。。。。。
你觉得碍眼,python的开发者也觉得碍眼,所以就为我们提供了一句语法糖来解决这个问题!
import time def timmer(func): def inner(): start_time=time.time() func() stop_time=time.time() print('执行时间是 %s'%(stop_time-start_time)) return inner @timmer #===>相当于func=timmer(func) def func(): time.sleep(0.01) print ("hello,大家好") #func=timmer(func) func()
装饰器语法糖就是@ ,是在定义函数的前面加,而不是在调用函数的时候加。
现在我们就可以简单的总结一下:
装饰器的本质:一个闭包函数
闭包函数 —— 在内部函数引用外部函数的变量
装饰器的功能:在不修改原函数及其调用方式的情况下对原函数功能进行扩展
但是还有一个问题要解决,刚刚我们讨论的装饰器都是装饰不带参数的函数,现在要装饰一个带参数的函数怎么办呢?
import time def timmer(func): def inner(name): start_time=time.time() func(name) stop_time=time.time() print('执行时间是 %s'%(stop_time-start_time)) return inner @timmer #===>相当于func=timmer(func)=inner def func(name): time.sleep(0.01) print ("hello,%s" %name) #func=timmer(func) func("lily") #===>相当于inner("lily")
其实装饰带参的函数并不是什么难事,但假如你有两个函数,需要传递的参数不一样了呢? 传递的参数不止一个,而且不仅位置参数,还有关键字参数
import time def timmer(func): def inner(*args,**kwargs): start_time=time.time() func(*args,**kwargs) stop_time=time.time() print('执行时间是 %s'%(stop_time-start_time)) return inner @timmer #===>相当于func=timmer(func)=inner def func(name): time.sleep(0.01) print ("hello,%s" %name) @timmer #===>相当于func1=timmer(func)=inner def func1(name,name1,name2="hanmeimei"): time.sleep(0.01) print ("hello,%s %s and %s" %(name,name1,name2)) #func=timmer(func) func("lily") #===>相当于inner("lily") func1("lilei","lily")
现在参数的问题已经完美的解决了,可是如果你的函数是有返回值的呢?
import time def timmer(func): def inner(*args,**kwargs): start_time=time.time() ret=func(*args,**kwargs) stop_time=time.time() print('执行时间是 %s'%(stop_time-start_time)) return ret return inner @timmer #===>相当于func=timmer(func)=inner def func(name): time.sleep(0.01) print ("hello,%s" %name) return "很高兴认识你!!!" print(func("lily"))
上面的装饰器已经非常完美了,但是正常我们情况下能查看函数的一些信息的方法用装饰器就都会失效
def index(): '''这是一个主页信息''' print('from index') print(index.__doc__) #查看函数注释的方法 print(index.__name__) #查看函数名的方法
为了不让他们失效,我们还要在装饰器上加上一点来完善它:
from functools import wraps #从functools导入wraps def deco(func): @wraps(func) #加在最内层函数正上方 def wrapper(*args,**kwargs): '''执行函数之前要做的''' return func(*args,**kwargs) '''执行函数之后要做的''' return wrapper @deco def index(): '''哈哈哈哈''' print('from index') print(index.__doc__) print(index.__name__)
没有加 from functools import wraps 和 @wraps(func)这两行代码,
执行的结果是
执行函数之前要做的
wrapper
加上 from functools import wraps 和 @wraps(func)这两行代码,
执行的结果是
哈哈哈哈
index
2.开放封闭原则
1.对扩展是开放的
为什么要对扩展开放呢?
我们说,任何一个程序,不可能在设计之初就已经想好了所有的功能并且未来不做任何更新和修改。所以我们必须允许代码扩展、添加新功能。
2.对修改是封闭的
为什么要对修改封闭呢?
就像我们刚刚提到的,因为我们写的一个函数,很有可能已经交付给其他人使用了,如果这个时候我们对其进行了修改,很有可能影响其他已经在使用该函数的用户。
装饰器完美的遵循了这个开放封闭原则。
3.装饰器的主要功能和装饰器的固定结构
装饰器的主要功能:
在不改变函数调用方式的基础上在函数的前、后添加功能。
装饰器的固定格式:
def timer(func): def inner(*args,**kwargs): '''执行函数之前要做的''' re = func(*args,**kwargs) '''执行函数之后要做的''' return re return inner @timer def func(*args,**kwargs): pass
from functools import wraps def deco(func): @wraps(func) #加在最内层函数正上方 def wrapper(*args,**kwargs): '''执行函数之前要做的''' re=func(*args,**kwargs) '''执行函数之之后要做的''' return re return wrapper @deco def func(*args,**kwargs): pass
4.带参数的装饰器
假如你有成千上万个函数使用了一个装饰器,现在你想把这些装饰器都取消掉,你要怎么做?
一个一个的取消掉? 没日没夜忙活3天。。。
过两天需要再加上这个装饰器。。。就该疯了。。。。
def outer(flag): def timer(func): def inner(*args,**kwargs): if flag: print('''执行函数之前要做的''') re = func(*args,**kwargs) print('''执行函数之后要做的''') else: re = func(*args,**kwargs) return re return inner return timer
5.多个装饰器装饰同一个函数
有些时候,我们也会用到多个装饰器装饰同一个函数的情况。
def wrapper1(func): def inner(): print('wrapper1 ,before func') func() print('wrapper1 ,after func') return inner def wrapper2(func): def inner(): print('wrapper2 ,before func') func() print('wrapper2 ,after func') return inner @wrapper2 @wrapper1 def f(): print('in f') f()
执行的结果:
wrapper2 ,before func
wrapper1 ,before func
in f
wrapper1 ,after func
wrapper2 ,after func


浙公网安备 33010602011771号