简言之,python装饰器就是用于拓展原来函数功能的一种函数,这个函数的特殊之处在于它的返回值也是一个函数,使用python装饰器的好处就是在不用更改原函数的代码前提下给函数增加新的功能。
一般而言,我们要想拓展原来函数代码,最直接的办法就是侵入代码里面修改,例如:
1. import time
2. def func():
3. print("hello")
4. time.sleep(1)
5. print("world")
这是我们最原始的的一个函数,然后我们试图记录下这个函数执行的总时间,那最简单的做法就是:
1. #原始侵入,篡改原函数
2. import time
3. def func():
4. startTime = time.time()
5.
6. print("hello")
7. time.sleep(1)
8. print("world")
9. endTime = time.time()
10.
11. msecs = (endTime - startTime)*1000
12. print("time is %d ms" %msecs)
但是如果你的Boss在公司里面和你说:这段代码是我们公司的核心代码,你不能直接去改我们的核心代码。”那该怎么办呢,我们仿照装饰器先自己试着写一下:
1. #避免直接侵入原函数修改,但是生效需要再次执行函数
2. import time
3.
4. def deco(func):
5. startTime = time.time()
6. func()
7. endTime = time.time()
8. msecs = (endTime - startTime)*1000
9. print("time is %d ms" %msecs)
10.
11.
12. def func():
13. print("hello")
14. time.sleep(1)
15. print("world")
16.
17. if __name__ == '__main__':
18. f = func
19. deco(f)#只有把func()或者f()作为参数执行,新加入功能才会生效
20. print("f.__name__ is",f.__name__)#f的name就是func()
21. print()
22. #func()
这里我们定义了一个函数deco,它的参数是一个函数,然后给这个函数嵌入了计时功能。然后你可以拍着胸脯对老板说,看吧,不用动你原来的代码,我照样拓展了它的函数功能。
然后你的老板有对你说:我们公司核心代码区域有一千万个func()函数,从func01()到func1kw(),按你的方案,想要拓展这一千万个函数功能,就是要执行一千万次deco()函数,这可不行呀,我心疼我的机器。”
好了,你终于受够你老板了,准备辞职了,然后你无意间听到了装饰器这个神器,突然发现能满足你闫博士的要求了。
我们先实现一个最简陋的装饰器,不使用任何语法糖和高级语法,看看装饰器最原始的面貌:
1. #既不需要侵入,也不需要函数重复执行
2. import time
3.
4. def deco(func):
5. def wrapper():
6. startTime = time.time()
7. func()
8. endTime = time.time()
9. msecs = (endTime - startTime)*1000
10. print("time is %d ms" %msecs)
11. return wrapper
12.
13.
14. @deco
15. def func():
16. print("hello")
17. time.sleep(1)
18. print("world")
19.
20. if __name__ == '__main__':
21. f = func #这里f被赋值为func,执行f()就是执行func()
22. f()
这里的deco函数就是最原始的装饰器,它的参数是一个函数,然后返回值也是一个函数。其中作为参数的这个函数func()就在返回函数wrapper()的内部执行。然后在函数func()前面加上@deco,func()函数就相当于被注入了计时功能,现在只要调用func(),它就已经变身为“新的功能更多”的函数了。
所以这里装饰器就像一个注入符号:有了它,拓展了原来函数的功能既不需要侵入函数内更改代码,也不需要重复执行原函数。
1. #带有参数的装饰器
2. import time
3.
4. def deco(func):
5. def wrapper(a,b):
6. startTime = time.time()
7. func(a,b)
8. endTime = time.time()
9. msecs = (endTime - startTime)*1000
10. print("time is %d ms" %msecs)
11. return wrapper
12.
13.
14. @deco
15. def func(a,b):
16. print("hello,here is a func for add :")
17. time.sleep(1)
18. print("result is %d" %(a+b))
19.
20. if __name__ == '__main__':
21. f = func
22. f(3,4)
23. #func()
然后你满足了Boss的要求后,Boss又说:我让你拓展的函数好多可是有参数的呀,有的参数还是个数不定的那种,你的装饰器搞的定不?”然后你嘿嘿一笑,深藏功与名!
1. #带有不定参数的装饰器
2. import time
3.
4. def deco(func):
5. def wrapper(*args, **kwargs):
6. startTime = time.time()
7. func(*args, **kwargs)
8. endTime = time.time()
9. msecs = (endTime - startTime)*1000
10. print("time is %d ms" %msecs)
11. return wrapper
12.
13.
14. @deco
15. def func(a,b):
16. print("hello,here is a func for add :")
17. time.sleep(1)
18. print("result is %d" %(a+b))
19.
20. @deco
21. def func2(a,b,c):
22. print("hello,here is a func for add :")
23. time.sleep(1)
24. print("result is %d" %(a+b+c))
25.
26.
27. if __name__ == '__main__':
28. f = func
29. func2(3,4,5)
30. f(3,4)
31. #func()
最后,你的老板说:可以的,我这里一个函数需要加入很多功能,一个装饰器怕是搞不定,装饰器能支持多个嘛,最后你就把这段代码丢给了他:
#多个装饰器
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
'''
多个装饰器执行的顺序就是从第一个装饰器开始,执行到最后一个装饰器,再执行函数本身。