你會Python嘛? 我會! 那你給我講下Python裝飾器吧! Python裝飾器啊?我沒用過哎
以上是我一個哥們面試時候發生的真實對白。python
———————————————-分割線——————————————————————————面試
簡言之,python裝飾器就是用於拓展原來函數功能的一種函數,這個函數的特殊之處在於它的返回值也是一個函數,使用python裝飾器的好處就是在不用更改原函數的代碼前提下給函數增長新的功能。
通常而言,咱們要想拓展原來函數代碼,最直接的辦法就是侵入代碼裏面修改,例如:app
import time def func(): print("hello") time.sleep(1) print("world")
這是咱們最原始的的一個函數,而後咱們試圖記錄下這個函數執行的總時間,那最簡單的作法就是:函數
#原始侵入,篡改原函數 import time def func(): startTime = time.time() print("hello") time.sleep(1) print("world") endTime = time.time() msecs = (endTime - startTime)*1000 print("time is %d ms" %msecs)
可是若是你的Boss在公司裏面和你說:「小祁,這段代碼是咱們公司的核心代碼,你不能直接去改咱們的核心代碼。」那該怎麼辦呢,咱們仿照裝飾器先本身試着寫一下:this
#避免直接侵入原函數修改,可是生效須要再次執行函數 import time def deco(func): startTime = time.time() func() endTime = time.time() msecs = (endTime - startTime)*1000 print("time is %d ms" %msecs) def func(): print("hello") time.sleep(1) print("world") if __name__ == '__main__': f = func deco(f)#只有把func()或者f()做爲參數執行,新加入功能纔會生效 print("f.__name__ is",f.__name__)#f的name就是func() print() #func()
這裏咱們定義了一個函數deco,它的參數是一個函數,而後給這個函數嵌入了計時功能。而後你能夠拍着胸脯對老闆說,看吧,不用動你原來的代碼,我照樣拓展了它的函數功能。
而後你的老闆有對你說:「小祁,咱們公司核心代碼區域有一千萬個func()函數,從func01()到func1kw(),按你的方案,想要拓展這一千萬個函數功能,就是要執行一千萬次deco()函數,這可不行呀,我心疼個人機器。」
好了,你終於受夠你老闆了,準備辭職了,而後你無心間聽到了裝飾器這個神器,忽然發現能知足你閆博士的要求了。
咱們先實現一個最簡陋的裝飾器,不使用任何語法糖和高級語法,看看裝飾器最原始的面貌:.net
#既不須要侵入,也不須要函數重複執行 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被賦值爲func,執行f()就是執行func() f()
這裏的deco函數就是最原始的裝飾器,它的參數是一個函數,而後返回值也是一個函數。其中做爲參數的這個函數func()就在返回函數wrapper()的內部執行。而後在函數func()前面加上@deco,func()函數就至關於被注入了計時功能,如今只要調用func(),它就已經變身爲「新的功能更多」的函數了。
因此這裏裝飾器就像一個注入符號:有了它,拓展了原來函數的功能既不須要侵入函數內更改代碼,也不須要重複執行原函數。code
#帶有參數的裝飾器 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()
而後你知足了Boss的要求後,Boss又說:「小祁,我讓你拓展的函數好多但是有參數的呀,有的參數仍是個數不定的那種,你的裝飾器搞的定不?」而後你嘿嘿一笑,深藏功與名!get
#帶有不定參數的裝飾器 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()
最後,你的老闆說:「能夠的,小祁,我這裏一個函數須要加入不少功能,一個裝飾器怕是搞不定,裝飾器能支持多個嘛」
最後你就把這段代碼丟給了他:class
#多個裝飾器 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 '''
多個裝飾器執行的順序就是從最後一個裝飾器開始,執行到第一個裝飾器,再執行函數自己。test
盜用評論裏面一位童鞋的例子:
def dec1(func): print("1111") def one(): print("2222") func() print("3333") return one def dec2(func): print("aaaa") def two(): print("bbbb") func() print("cccc") return two @dec1 @dec2 def test(): print("test test") test()
輸出:
aaaa 1111 2222 bbbb test test cccc 3333
參考:Python 裝飾器:http://python.jobbole.com/82344/