decorator是用來在代碼運行期間動態增長功能的,本質上是一個返回函數的高階函數。假設如今有這樣一種需求,即在每一個函數調用前記錄日誌,記錄被調用的函數名稱,能夠這樣實現:python
def log(func): def wrapper(*args, **kwargs): print "CALL %s()" % func.__name__ return func(*args, **kwargs) return wrapper def sayHi(): print "Hi, Buddy." def sayHello(): print "Hello, Buddy." # 調用函數,記錄日誌 log(sayHi)() # 輸出爲 # CALL sayHi() # Hi, Buddy. log(sayHello)() # 輸出爲 # CALL sayHello() # Hello, Buddy.
這種方法確實實現了記錄日誌的功能,但每次這麼調用未免太過繁瑣,decorator所以出現。app
其實,以前定義的log
函數即爲一個decorator,只是使用方式不正確:ide
@log def sayHi(): print "Hi, Buddy." sayHi() # 輸出爲 # CALL sayHi() # Hi, Buddy. @log def sayHello(): print "Hello, Buddy." sayHello() # 輸出爲 # CALL sayHello() # Hello, Buddy.
能夠看到,使用decorator很是簡單方便,def sayHi():
前的@log
至關於將sayHi
做爲參數傳入log
函數中,並將返回值賦給sayHi
,即:函數
sayHi = log(sayHi)
可是細心的讀者不難發現,這樣一來函數sayHi
的__name__
屬性發生變化,由以前的sayHi
變爲wrapper
,使用python內置的functools.wraps
方法能夠解決這一問題,改造後的decorator以下:網站
import functools def log(func): @functools.wraps(func) def wrapper(*args, **kwargs): print "CALL %s()" % func.__name__ return func(*args, **kwargs) return wrapper
讓咱們更進一步,使用三層嵌套的decorator,容許再多傳入一次參數:spa
import functools def log(text="CALL") def decorator(func): @functools.wraps(func) def wrapper(*args, **kwargs): print "%s %s()" % (text, func.__name__) return func(*args, **kwargs) return wrapper return decorator
此次咱們能夠選擇傳入一個表示函數運行狀態的字符串,因爲多了一層嵌套,使用時也會有些變化:日誌
@log("EXECUTE") def sayHi(): print "Hi, Buddy." sayHi() # 輸出爲 # EXECUTE sayHi() # Hi, Buddy.
修改事後嵌套使用爲:code
sayHi = log("EXECUTE")(sayHi)
修改log
函數,使該decorator既能夠經過字符串
@log
使用,又能夠經過get
@log("EXECUTE")
使用:
import functools def log(text="CALL"): if callable(text): func = text @functools.wraps(func) def wrapper(*args, **kwargs): print "CALL %s()" % func.__name__ return func(*args, **kwargs) else: def decorator(func): @functools.wraps(func) def wrapper(*args, **kwargs): print "%s %s()" % (text, func.__name__) return func(*args, **kwargs) return wrapper return decorator
兩種使用方式:
@log def sayHi(): print "Hi, Buddy." sayHi() # 輸出爲 # CALL sayHi() # Hi, Buddy. @log("EXECUTE") def sayHello(): print "Hello, Buddy." sayHello() # 輸出爲 # EXECUTE sayHello() # Hello, Buddy.
修改log
函數,使該decorator在函數調用前及函數調用後分別輸出一條日誌:
import functools def log(func): def wrapper(*args, **kwargs): print "CALL BEGINNING" call = func(*args, **kwargs) print "CALL ENDING" return call return wrapper
使用該decorator:
@log def sayHi(): print "Hi, Buddy." sayHi() # 輸出爲 # CALL BEGINING # Hi, Buddy. # CALL ENDING