標籤:python decorator
1. 介紹
decorator是用來在代碼運行期間動態增加功能的,本質上是一個返回函數的高階函數。假設現在有這樣一種需求,即在每個函數調用前記錄日誌,記錄被調用的函數名稱,可以這樣實現:
def log(func): def wrapper(*args, **kwargs): print "CALL %s()" % func.__name__ return func(*args, **kwargs) return wrapperdef sayHi(): print "Hi, Buddy."def sayHello(): print "Hello, Buddy."# 調用函數,記錄日誌log(sayHi)()# 輸出為# CALL sayHi()# Hi, Buddy.log(sayHello)()# 輸出為# CALL sayHello()# Hello, Buddy.
這種方法確實實現了記錄日誌的功能,但每次這麼調用未免太過繁瑣,decorator因此出現。
2. 使用
其實,之前定義的log函數即為一個decorator,只是使用方式不正確:
@logdef sayHi(): print "Hi, Buddy."sayHi()# 輸出為# CALL sayHi()# Hi, Buddy.@logdef 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,允許再多傳入一次參數:
import functoolsdef 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.
修改過後嵌套使用為:
sayHi = log("EXECUTE")(sayHi)3. 拓展3.1
修改log函數,使該decorator既可以通過
@log
使用,又可以通過
@log("EXECUTE")
使用:
import functoolsdef 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
兩種使用方式:
@logdef sayHi(): print "Hi, Buddy."sayHi()# 輸出為# CALL sayHi()# Hi, Buddy.@log("EXECUTE")def sayHello(): print "Hello, Buddy."sayHello()# 輸出為# EXECUTE sayHello()# Hello, Buddy.3.2
修改log函數,使該decorator在函數調用前及函數調用後分別輸出一條日誌:
import functoolsdef log(func): def wrapper(*args, **kwargs): print "CALL BEGINNING" call = func(*args, **kwargs) print "CALL ENDING" return call return wrapper
使用該decorator:
@logdef sayHi(): print "Hi, Buddy."sayHi()# 輸出為# CALL BEGINING# Hi, Buddy.# CALL ENDING
參考資料:
廖雪峰的官方網站
本文出自 “細桶假狗屎” 部落格,請務必保留此出處http://xitongjiagoushi.blog.51cto.com/9975742/1683554
初窺Python(五)——python中的decorator