一、装饰器的作用
装饰器实际上就是为了给某程序增添功能,但该程序已经上线或已经被使用,那么就不能大批量的修改源代码,这样是不科学的也是不现实的,因为就产生了装饰器,使得其满足:
- 不能修改被装饰的函数的源代码
- 不能修改被装饰的函数的调用方式
- 满足1、2的情况下给程序增添功能
概括的讲,装饰器的作用就是在不修改原代码的条件下为已经存在的函数或对象添加功能。
二、装饰器实现
写一个装饰器记录函数执行的时间
1、最简单的装饰器,不带参数的
两层函数,外层函数包裹内层函数,内层函数调用了外层函数的变量,外层函数返回内层函数的函数名;被装饰函数作为参数传入装饰器。
import time def deco(func): def wrapper(): startTime = time.time() func() endTime = time.time() msecs = (endTime - startTime)*1000 print("time is %d ms" %msecs) return wrapper @deco def func(): print("hello") time.sleep(1) print("world") if __name__ == '__main__': f = func f()
这里的deco函数就是最原始的装饰器,它的参数是一个函数,然后返回值也是一个函数。其中作为参数的这个函数func()就在返回函数wrapper()的内部执行。然后在函数func()前面加上@deco,func()函数就相当于被注入了计时功能,现在只要调用func(),它就已经变身为“新的功能更多”的函数了。
所以这里装饰器就像一个注入符号:有了它,拓展了原来函数的功能既不需要侵入函数内更改代码,也不需要重复执行原函数。
装饰带参数函数的装饰器:
#带有参数的装饰器 import time def deco(func): def wrapper(a,b): startTime = time.time() func(a,b) endTime = time.time() msecs = (endTime - startTime)*1000 print("time is %d ms" %msecs) return wrapper @deco def func(a,b): print("hello,here is a func for add :") time.sleep(1) print("result is %d" %(a+b)) if __name__ == '__main__': f = func f(3,4) #func()
装饰带有不定参数函数的装饰器:
#带有不定参数的装饰器 import time def deco(func): def wrapper(*args, **kwargs): startTime = time.time() func(*args, **kwargs) endTime = time.time() msecs = (endTime - startTime)*1000 print("time is %d ms" %msecs) return wrapper @deco def func(a,b): print("hello,here is a func for add :") time.sleep(1) print("result is %d" %(a+b)) @deco def func2(a,b,c): print("hello,here is a func for add :") time.sleep(1) print("result is %d" %(a+b+c)) if __name__ == '__main__': f = func func2(3,4,5) f(3,4) #func()
2、带参数的装饰器
给装饰器传参:
再提一层函数. 专门给装饰器传递参数用的.
当装饰器装饰函数后元数据会发生改变如何保护元数据呢: 给内存函数加@wraps(func)装饰器
1 from functools import wraps 2 3 def wrapper_out(flag): 4 def wrapper(func): 5 @wraps(func) # 保护元数据 6 def inner(*args, **kwargs): 7 if flag: 8 result = func(*args, **kwargs) 9 print(func.__name__) # test 10 return result 11 else: 12 print("by by") 13 return inner 14 return wrapper 15 16 @wrapper_out(1) #@wrapper, flag->1 ==> test=wrapper(test),==> test=inner, flag->1,func->test 17 def test(): 18 print("I'm test") 19 20 test() 21 print(test.__name__) # 没有加@wraps(func)时打印inner, 加了打印test
@wrapper_out(1) 先执行后面的括号, 返回一个装饰器再和前面的@拼接.成一个语法糖
3、多个装饰器装饰一个函数
import time def deco01(func): def wrapper(*args, **kwargs): print("this is deco01") startTime = time.time() func(*args, **kwargs) endTime = time.time() msecs = (endTime - startTime)*1000 print("time is %d ms" %msecs) print("deco01 end here") return wrapper def deco02(func): def wrapper(*args, **kwargs): print("this is deco02") func(*args, **kwargs) print("deco02 end here") return wrapper @deco01 @deco02 def func(a,b): print("hello,here is a func for add :") time.sleep(1) print("result is %d" %(a+b)) if __name__ == '__main__': f = func f(3,4) #func() ''' this is deco01 this is deco02 hello,here is a func for add : result is 7 deco02 end here time is 1003 ms deco01 end here '''
上面例子中;当装饰器装载时,func函数被作为参数传给deco02,而deco02又作为参数传给deco01,装载装饰器相当于执行func=deco01(deco02(func)), 此时先执行deco02(func)定义了deco02中的wrapper函数将func指向被装饰的函数,并返回wrapper,执行deco01(wrapper)将func指向deco02的wrapperb并返回返回deco01的wrapper函数;然后进行赋值func=wrapper ,用函数替代了函数名func,当执行func时相当于开始执行deco01的wrapper, 打印“this is deco01” 当执行到deco01中的func(*args, **kwargs)时实际上执行的是deco02中的wrapper打印"this is deco02",当执行到deco02的func(*args, **kwargs)实际上执行的是func函数打印“hello,here is a func for add :”和“result is 7”,执行完func 继续往下执行打印“deco02 end here”,然后deco01中wrapper继续往下执行打印“”time is 1003ms“” 和 “”“deco01 end here”
理解多个装饰器另一个更简单的例子:
1 def dec1(func): 2 print("1111") 3 def one(): 4 print("2222") 5 func() 6 print("3333") 7 return one 8 9 def dec2(func): 10 print("aaaa") 11 def two(): 12 print("bbbb") 13 func() 14 print("cccc") 15 return two 16 17 @dec1 18 @dec2 19 def test(): 20 print("test test") 21 22 test()
输出:
aaaa 1111 2222 bbbb test test cccc 3333
装饰器的外函数和内函数之间的语句是没有装饰到目标函数上的,而是在装载装饰器时的附加操作。 17~20行是装载装饰器的过程,相当于执行了test=dect1(dect2(test)),此时先执行dect2(test),结果是输出aaaa、将func指向函数test、并返回函数two,然后执行dect1(two),结果是输出1111、将func指向函数two、并返回函数one,然后进行赋值,用函数替代了函数名test。 22行则是实际调用被装载的函数,这时实际上执行的是函数one,运行到func()时执行函数two,再运行到func()时执行未修饰的函数test。
因此多个装饰器装饰一个函数的执行顺序为:
@wrapper_1 @wrapper_2 @wrapper_3 def yue(): print("约") yue()
# wrapper_1 wrapper_2 wrapper_3 yue wrapper_3 wrapper_2 wrapper_1
类似于{ [ ( 0 ) ] }