函數是個對象,而且能夠賦值給一個變量,經過變量也能調用該函數:python
>>> def now(): ... print('2017-12-28') ... >>> l = now >>> l() 2017-12-28
利用函數的_name_屬性,能夠拿到函數的名字:app
>>> now.__name__ 'now' >>> l.__name__ 'now'
若是咱們在調用函數now()先後自動打印日誌,但又不容許修改now()函數的定義——在代碼運行期間動態增長功能的方式,稱之爲‘裝飾器’Decorator。函數
好比,定義一個能打印日誌的decorator:測試
>>> def log(func): ... def wrapper(*args,**kw): ... print('call %s():' % func.__name__) ... return func(*args,**kw) ... return wrapper ...
觀察log 函數,發現,本質上這就是一個返回函數的高階函數!log做爲一個decorator,接收一個函數做爲參數,冰飯一個函數。藉助python的@語法,把decorator置於函數的定義的地方:spa
>>> @log ... def now(): ... print('2017-12-28') ... >>> now() call now(): 2017-12-28
在調用now()函數時候,不只運行了now函數,還會在此以前打印一行日誌。設計
其實,把@log放到now()函數的定義前,至關於執行了:日誌
now = log(now)code
log是一個decorator,返回一個函數,返回的這個函數名字叫wrapper,原來的now()函數還存在,這個時候now變量指向了這個返回函數wrapper。當調用now()將執行新的函數wrapper函數。wrapper函數的參數是(*args,**kw),所以wrapper()函數能夠接受任意參數!在wrapper函數內部,首先打印日誌,再緊接着調用原始函數。對象
分割線-------------------------------blog
若是decorator自己須要傳入參數,那就須要編寫一個返回decorator的高階函數。好比要自定義log的文本,定義、用法和結果:
>>> def log(text): ... def decorator(func): ... def wrapper(*args,**kw): ... print('%s %s():' %(text,func.__name__)) ... return func(*args,**kw) ... return wrapper ... return decorator ... >>> @log('執行') ... def now(): ... print('2017-12-28') ... >>> now() 執行 now(): 2017-12-28
前面的例子中包含了兩層def嵌套,後面的例子中包含了三層def嵌套。其實,三層嵌套的效果相似:
now = log('執行')(now)
解析:首先執行log('執行'),返回的是decorator函數,再調用返回函數,參數是now函數,最終的返回值是wrapper函數。
可是,咱們執行下面語句來測試:
>>> now.__name__ 'wrapper'
咱們發現:通過decorator裝飾後的函數,他們的__name__屬性已經從now變成了wrapper。這是由於返回的那個函數wrapper函數名字就是wrapper,因此,須要把原始函數的__name__屬性複製到wrapper函數中,不然,其餘一些依賴函數簽名的代碼執行就會報錯。
實際上咱們並不須要編寫wrapper.__name__ = func.__name__這樣的代碼,python內置了functools.wraps就是爲了這個。最
最後一步,一個完整的decorator的寫法以下:
>>> import functools >>> def log(func): ... @functools.wraps(func) ... def wrapper(*args,**kw): ... print('執行 %s()' %func.__name__) ... return func(*args,**kw) ... return wrapper ... >>> @log ... def now(): ... print('日誌') ... >>> now <function now at 0x03317198> >>> now() 執行 now() 日誌
針對帶有參數的decorator:
>>> import functools >>> def log(text): ... def decorator(func): ... @functools.wraps(func) ... def wrapper(*args,**kw): ... print('%s %s()' %(text,func.__name__)) ... return func(*args,**kw) ... return wrapper ... return decorator ... >>> @log('ABC') ... def now(): ... print('這麼複雜幹嗎') ... >>> now() ABC now() 這麼複雜幹嗎
例子:設計一個decorator,可做用於任何函數上,並打印該函數的執行時間:
>>> import time,functools >>> def log(func): ... @functools.wraps(func) ... def wrapper(*args,**kw): ... t1 = time.time() ... r = func(*args,**kw) ... print('%s excute in %s ms'%(func.__name__,1000*(time.time()-t1))) ... return r ... return wrapper ... >>> @log ... def fast(x,y): ... return x+y ... >>> @log ... def slow(x,y,z): ... time.sleep(0.1234) ... return x*y*z ... >>> @log ... def fast(x,y): ... time.sleep(0.0012) ... return x+y ... >>> fast(3,5) fast excute in 2.0973682403564453 ms 8 >>> slow(4,5,6) slow excute in 124.2520809173584 ms 120