Python編寫(xiě)裝飾器,給任意函數(shù)計(jì)時(shí)
在開(kāi)發(fā)代碼時(shí),經(jīng)常有這樣的需求,想知道調(diào)用一個(gè)函數(shù)的入?yún)?、返回值、花費(fèi)時(shí)間。
對(duì)應(yīng)這個(gè)調(diào)用,我們可以自己在函數(shù)開(kāi)頭打印參數(shù)、末尾打印結(jié)果、結(jié)尾時(shí)間減去開(kāi)始時(shí)間計(jì)時(shí)。
然而這樣的代碼,其實(shí)任何函數(shù)都一樣,有沒(méi)有辦法寫(xiě)一次代碼多次調(diào)用呢。
這其實(shí)就是“給任何函數(shù),增加一個(gè)公共的附加特性”,可以用Python的解釋器解決。
# 1、導(dǎo)入相關(guān)的包
import functoolsimport loggingimport timelog_format = "%(asctime)s %(message)s"logging.basicConfig(format=log_format,level=logging.INFO)
這用到了functools包,它有wraps方法,是裝飾需要的函數(shù)。
# 2、編寫(xiě)裝飾器
def time_logger(func):def wrapper_timer(*args, **kwargs):logging.info(f"BeginFunction {func.__name__!r}, args={args}, kwargs={kwargs}")start_time = time.perf_counter() # 1value = func(*args, **kwargs)end_time = time.perf_counter() # 2run_time = end_time - start_time # 3logging.info(f"EndFunction {func.__name__!r} in {run_time:.4f} secs")return valuereturn wrapper_timer
大家注意,這個(gè)裝飾器會(huì)被任何函數(shù)使用。其中的func(*args, **kwargs)中的func就是目標(biāo)函數(shù),args、kwargs是這個(gè)函數(shù)調(diào)用的參數(shù)。
這就實(shí)現(xiàn)了一個(gè)裝飾器。
# 3、使用裝飾器給函數(shù)計(jì)時(shí)
def my_func(name, age):logging.info(f"my func begin, {name}, {age}")time.sleep(2)logging.info(f"my func end, {name}, {age}")
@time_logger就是計(jì)時(shí)器的裝飾器使用方式,任何函數(shù)只需要這個(gè)簡(jiǎn)單地一行代碼,就可以加上這個(gè)功能。
# 4、我們來(lái)看看調(diào)用效果
執(zhí)行如下代碼:
my_func("antpython", "99")打印結(jié)果為:
2021-05-17 20:30:26,294 BeginFunction 'my_func', args=('antpython', '99'), kwargs={}2021-05-17 20:30:26,294 my func begin, antpython, 992021-05-17 20:30:28,295 my func end, antpython, 992021-05-17 20:30:28,295 EndFunction 'my_func' in 2.0007 secs
其中第一行BeginFunction、最后一行EndFunction,都是裝飾器里面的輸出。
而my func begin、my func end,則是自己的函數(shù)的輸出。
# 5、最后的總結(jié)
對(duì)于裝飾器,可以有很多用途:
WEB服務(wù)中檢測(cè)用戶(hù)是否登錄
加入所有函數(shù)的日志、計(jì)時(shí)功能
對(duì)所有訪問(wèn)數(shù)據(jù)庫(kù)的函數(shù)加上入侵檢測(cè)功能
對(duì)用戶(hù)的訪問(wèn)請(qǐng)求做攻擊檢測(cè)
自動(dòng)處理數(shù)據(jù)庫(kù)的鏈接提取、提交、回滾等操作
只要是一個(gè)通用的功能,都可以用裝飾器來(lái)解決。這在其他語(yǔ)言比如JAVA中也叫做攔截器、代理等功能。
這是一個(gè)很有用的特性,趕快試試吧!
