Python装饰器
什么是装饰器
装饰器本质上就是一个python函数,他可以让其他函数在不需要做任何代码变动的前提下,增加额外的功能,装饰器的返回值也是一个函数对象。
装饰器的应用场景:比如插入日志,性能测试,事务处理,缓存等等场景。
装饰器的形成过程
现在我有一个需求,我想让你测试这个函数的执行时间,在不改变这个函数代码的情况下:
import time
#要测试的代码
def func1():
print('in func1')
#测试代码
def timer(func):
def inner():
start = time.time()
func()
print(time.time() - start)
return inner
func1 = timer(func1)
func1()
但是如果有多个函数,我都想让你测试他们的执行时间,你每次是不是都得func1 = timer(func1)?这样还是有点麻烦,因为这些函数的函数名可能是不相同,有func1,func2,graph,等等,所以更简单的方法,python给你提供了,那就是语法糖。
import time
def timer(func):
def inner():
start = time.time()
func()
print(time.time() - start)
return inner
@timer #==> func1 = timer(func1)
def func1():
print('in func1')
func1()
刚刚我们讨论的装饰器都是装饰不带参数的函数,现在要装饰一个带参数的函数怎么办呢?
def timer(func):
def inner(a):
start = time.time()
func(a)
print(time.time() - start)
return inner
@timer
def func1(a):
print(a)
func1(1)
import time
def timer(func):
def inner(*args,**kwargs):
start = time.time()
re = func(*args,**kwargs)
print(time.time() - start)
return re
return inner
@timer #==> func1 = timer(func1)
def func1(a,b):
print('in func1')
@timer #==> func2 = timer(func2)
def func2(a):
print('in func2 and get a:%s'%(a))
return 'fun2 over'
func1('aaaaaa','bbbbbb')
print(func2('aaaaaa'))
上面的装饰器已经非常完美了,但是有我们正常情况下查看函数信息的方法在此处都会失效:
def index():
'''这是一个主页信息'''
print('from index')
print(index.__doc__) #查看函数注释的方法
print(index.__name__) #查看函数名的方法
如何解决呢?
from functools import 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__)
开放封闭原则
1.对扩展是开放的
为什么要对扩展开放呢?
我们说,任何一个程序,不可能在设计之初就已经想好了所有的功能并且未来不做任何更新和修改。所以我们必须允许代码扩展、添加新功能。
2.对修改是封闭的
为什么要对修改封闭呢?
就像我们刚刚提到的,因为我们写的一个函数,很有可能已经交付给其他人使用了,如果这个时候我们对其进行了修改,很有可能影响其他已经在使用该函数的用户。
装饰器完美的遵循了这个开放封闭原则。
装饰器的主要功能和固定结构
def timer(func):
def inner(*args,**kwargs):
'''执行函数之前要做的'''
re = func(*args,**kwargs)
'''执行函数之后要做的'''
return re
return inner
from functools import wraps
def deco(func):
@wraps(func) #加在最内层函数正上方
def wrapper(*args,**kwargs):
return func(*args,**kwargs)
return wrapper
带参数的装饰器
假如你有成千上万个函数使用了一个装饰器,现在你想把这些装饰器都取消掉,你要怎么做?
一个一个的取消掉? 没日没夜忙活3天。。。
过两天你领导想通了,再让你加上。。。
import time
def outer(flag):
def timer(func):
def inner(*args,**kwargs):
if flag:
print('''执行函数之前要做的''')
re = func(*args,**kwargs)
if flag:
print('''执行函数之后要做的''')
return re
return inner
return timer
@outer(False)
def func():
print(111)
func()
flag = False #如果是False便不会进行测试代码的执行时间
import time
def timmerout(flag1): # flag1 =flag
def timmer(f):
def inner(*args,**kwargs):
if flag1:
start_time = time.time()
ret = f(*args,**kwargs)
end_time = time.time()
print('此函数的执行效率%s' % (end_time - start_time))
return ret
else:
ret = f(*args, **kwargs)
return ret
return inner
return timmer
@timmerout(flag)
def JDshop():
time.sleep(0.3)
print('非常复杂......')
return 666
JDshop()
@timmerout(flag)
def JD():
time.sleep(0.3)
print('非常复杂......')
return 666
JD()
@timmerout(flag)
def taobao():
time.sleep(0.1)
print('非常复杂......')
return 666
taobao()
@timmerout(flag)
def taobaoshop():
time.sleep(0.1)
print('非常复杂......')
return 666
taobaoshop()
多个装饰器装饰一个函数
在执行下面的代码时先踩踩他们的执行顺序,当你执行下面的代码你会发他们执行顺序并不是你想的那样(大神除外)
def wrapper1(func): # func = f函数名
def inner1():
print('wrapper1 ,before func')
func() # f函数名()
print('wrapper1 ,after func')
return inner1
def wrapper2(func): # func = inner1
def inner2():
print('wrapper2 ,before func')
func() # inner1()
print('wrapper2 ,after func')
return inner2
@wrapper2 # f = warpper2(f) 里面的f是inner1 外面的f是inner2
@wrapper1 # f = warpper1(f) 里面的f函数名 外面的f 是inner1
def f():
print('in f') # 3
f() # inner2()
执行结果如下:接下来我们就聊聊他为什么这样执行。。。。。
wrapper2 ,before func
wrapper1 ,before func
in f
wrapper1 ,after func
wrapper2 ,after func
#首先他会先执行离函数最近的装饰器@wrapper1看到@就该想到f = wrapper1(f) 此时我们装饰器返回inner1此时他不会立即执行 他会先把所有装饰器装饰执行完后再执行里面的inner 此时@wrapper2也就是f = wrapper2(f)里面的f参数是inner1 外面的f是inner2 此时外面函数执行到函数下面的f()也就相当于执行inner2() 也就打印了“wrapper2 ,before func”当代码执行到inner2里面的func()此时里面所执行的就是inner1()便打印“wrapper1 ,before func”接着往下走当代码执行到inner1里面的func()此时func()等于函数名加()也就执行了函数得到结果“in f”接着将inner1装饰器走完得到“wrapper1 ,after func”最后去走完inner2里面的代码得到结果“wrapper2 ,after func”