1你会Python嘛? 2我会! 3那你给我讲下Python装饰器吧! 4Python装饰器啊?我没用过哎
以上是我一个哥们面试时候发生的真实对白。
----------------------------------------------分割线------------------------------------------------------------------------------
简言之,python装饰器就是用于拓展原来函数功能的一种函数,这个函数的特殊之处在于它的返回值也是一个函数,使用python装饰器的好处就是在不用更改原函数的代码前提下给函数增加新的功能。
一般而言,我们要想拓展原来函数代码,最直接的办法就是侵入代码里面修改,例如:
1import time 2def func(): 3 print("hello") 4 time.sleep(1) 5 print("world")
这是我们最原始的的一个函数,然后我们试图记录下这个函数执行的总时间,那最简单的做法就是:
1#原始侵入,篡改原函数 2import time 3def 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#避免直接侵入原函数修改,但是生效需要再次执行函数 2import time 3 4def 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 12def func(): 13 print("hello") 14 time.sleep(1) 15 print("world") 16 17if __name__ == '__main__': 18 f = func 19 deco(f)#只有把func()或者f()作为参数执行,新加入功能才会生效 20 print("f.__name__ is",f.__name__)#f的name就是func
这里我们定义了一个函数deco,它的参数是一个函数,然后给这个函数嵌入了计时功能。然后你可以拍着胸脯对老板说,看吧,不用动你原来的代码,我照样拓展了它的函数功能。
然后你的老板有对你说:“小祁,我们公司核心代码区域有一千万个func()函数,从func01()到func1kw(),按你的方案,想要拓展这一千万个函数功能,就是要执行一千万次deco()函数,这可不行呀,我心疼我的机器。”
好了,你终于受够你老板了,准备辞职了,然后你无意间听到了装饰器这个神器,突然发现能满足你闫博士的要求了。
我们先实现一个最简陋的装饰器,不使用任何语法糖和高级语法,看看装饰器最原始的面貌:
1#既不需要侵入,也不需要函数重复执行 2import time 3 4def 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 15def func(): 16 print("hello") 17 time.sleep(1) 18 print("world") 19 20if __name__ == '__main__': 21 f = func #这里f被赋值为func,执行f()就是执行func() 22 f()
这里的deco函数就是最原始的装饰器,它的参数是一个函数,然后返回值也是一个函数。其中作为参数的这个函数func()就在返回函数wrapper()的内部执行。然后在函数func()前面加上@deco,func()函数就相当于被注入了计时功能,现在只要调用func(),它就已经变身为“新的功能更多”的函数了。
所以这里装饰器就像一个注入符号:有了它,拓展了原来函数的功能既不需要侵入函数内更改代码,也不需要重复执行原函数。
1#带有参数的装饰器 2import time 3 4def 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 15def func(a,b): 16 print("hello,here is a func for add :") 17 time.sleep(1) 18 print("result is %d" %(a+b)) 19 20if __name__ == '__main__': 21 f = func 22 f(3,4) 23 #func()
然后你满足了Boss的要求后,Boss又说:“小祁,我让你拓展的函数好多可是有参数的呀,有的参数还是个数不定的那种,你的装饰器搞的定不?”然后你嘿嘿一笑,深藏功与名!
1#带有不定参数的装饰器 2import time 3 4def 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 15def 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 21def 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 27if __name__ == '__main__': 28 f = func 29 func2(3,4,5) 30 f(3,4) 31 #func()
最后,你的老板说:“可以的,小祁,我这里一个函数需要加入很多功能,一个装饰器怕是搞不定,装饰器能支持多个嘛"
最后你就把这段代码丢给了他:
1#多个装饰器 2 3import time 4 5def deco01(func): 6 def wrapper(*args, **kwargs): 7 print("this is deco01") 8 startTime = time.time() 9 func(*args, **kwargs) 10 endTime = time.time() 11 msecs = (endTime - startTime)*1000 12 print("time is %d ms" %msecs) 13 print("deco01 end here") 14 return wrapper 15 16def deco02(func): 17 def wrapper(*args, **kwargs): 18 print("this is deco02") 19 func(*args, **kwargs) 20 21 print("deco02 end here") 22 return wrapper 23 24@deco01 25@deco02 26def func(a,b): 27 print("hello,here is a func for add :") 28 time.sleep(1) 29 print("result is %d" %(a+b)) 30 31 32 33if __name__ == '__main__': 34 f = func 35 f(3,4) 36 #func() 37 38''' 39this is deco01 40this is deco02 41hello,here is a func for add : 42result is 7 43deco02 end here 44time is 1003 ms 45deco01 end here 46'''
多个装饰器执行的顺序就是从最后一个装饰器开始,执行到第一个装饰器,再执行函数本身。
盗用评论里面一位童鞋的例子:
1def dec1(func): 2 print("1111") 3 def one(): 4 print("2222") 5 func() 6 print("3333") 7 return one 8 9def dec2(func): 10 print("aaaa") 11 def two(): 12 print("bbbb") 13 func() 14 print("cccc") 15 return two 16 17@dec1 18@dec2 19def test(): 20 print("test test") 21 22test()
输出:
1aaaa 21111 32222 4bbbb 5test test 6cccc 73333
装饰器的外函数和内函数之间的语句是没有装饰到目标函数上的,而是在装载装饰器时的附加操作。
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。
参考:Python 装饰器:http://python.jobbole.com/82344/

本文转自 https://blog.csdn.net/xiangxianghehe/article/details/77170585,如有侵权,请联系删除。
