https://blog.csdn.net/xiangxianghehe/article/details/77170585
你會Python嘛?
我會!
那你給我講下Python裝飾器吧!
Python裝飾器啊?我沒用過哎
以上是我一個哥們面試時候發生的真實對白。
———————————————-分割線——————————————————————————
簡言之,python裝飾器就是用于拓展原來函數功能的一種函數,這個函數的特殊之處在于它的返回值也是一個函數,使用python裝飾器的好處就是在不用更改原函數的代碼前提下給函數增加新的功能。?
一般而言,我們要想拓展原來函數代碼,最直接的辦法就是侵入代碼里面修改,例如:
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)*1000print("time is %d ms" %msecs)
但是如果你的Boss在公司里面和你說:“小祁,這段代碼是我們公司的核心代碼,你不能直接去改我們的核心代碼。”那該怎么辦呢,我們仿照裝飾器先自己試著寫一下:
#避免直接侵入原函數修改,但是生效需要再次執行函數
import timedef deco(func):startTime = time.time()func()endTime = time.time()msecs = (endTime - startTime)*1000print("time is %d ms" %msecs)def func():print("hello")time.sleep(1)print("world")if __name__ == '__main__':f = funcdeco(f)#只有把func()或者f()作為參數執行,新加入功能才會生效print("f.__name__ is",f.__name__)#f的name就是func()print()#func()
這里我們定義了一個函數deco,它的參數是一個函數,然后給這個函數嵌入了計時功能。然后你可以拍著胸脯對老板說,看吧,不用動你原來的代碼,我照樣拓展了它的函數功能。?
然后你的老板有對你說:“小祁,我們公司核心代碼區域有一千萬個func()函數,從func01()到func1kw(),按你的方案,想要拓展這一千萬個函數功能,就是要執行一千萬次deco()函數,這可不行呀,我心疼我的機器。”?
好了,你終于受夠你老板了,準備辭職了,然后你無意間聽到了裝飾器這個神器,突然發現能滿足你閆博士的要求了。?
我們先實現一個最簡陋的裝飾器,不使用任何語法糖和高級語法,看看裝飾器最原始的面貌:
#既不需要侵入,也不需要函數重復執行
import timedef deco(func):def wrapper():startTime = time.time()func()endTime = time.time()msecs = (endTime - startTime)*1000print("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()
- eco函數就是最原始的裝飾器,它的參數是一個函數,然后返回值也是一個函數。其中作為參數的這個函數func()就在返回函數wrapper()的內部執行。然后在函數func()前面加上@deco,func()函數就相當于被注入了計時功能,現在只要調用func(),它就已經變身為“新的功能更多”的函數了。?
所以這里裝飾器就像一個注入符號:有了它,拓展了原來函數的功能既不需要侵入函數內更改代碼,也不需要重復執行原函數。
#帶有參數的裝飾器
import timedef deco(func):def wrapper(a,b):startTime = time.time()func(a,b)endTime = time.time()msecs = (endTime - startTime)*1000print("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 = funcf(3,4)#func()
然后你滿足了Boss的要求后,Boss又說:“小祁,我讓你拓展的函數好多可是有參數的呀,有的參數還是個數不定的那種,你的裝飾器搞的定不?”然后你嘿嘿一笑,深藏功與名!
#帶有不定參數的裝飾器
import timedef deco(func):def wrapper(*args, **kwargs):startTime = time.time()func(*args, **kwargs)endTime = time.time()msecs = (endTime - startTime)*1000print("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 = funcfunc2(3,4,5)f(3,4)#func()
- ?
- 老板說:“可以的,小祁,我這里一個函數需要加入很多功能,一個裝飾器怕是搞不定,裝飾器能支持多個嘛”?
最后你就把這段代碼丟給了他:
#多個裝飾器import timedef deco01(func):def wrapper(*args, **kwargs):print("this is deco01")startTime = time.time()func(*args, **kwargs)endTime = time.time()msecs = (endTime - startTime)*1000print("time is %d ms" %msecs)print("deco01 end here")return wrapperdef 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 = funcf(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
'''
多個裝飾器執行的順序就是從最后一個裝飾器開始,執行到第一個裝飾器,再執行函數本身。
盜用評論里面一位童鞋的例子:
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/