1. 裝飾器是什么?
裝飾器(Decorator )作為python語(yǔ)言面試的必免考點(diǎn)!如果沒(méi)有回答上來(lái),在面試官心中你的技術(shù)就大打折扣!
首先從字面上去理解,裝飾就是修飾,點(diǎn)綴的意思。相當(dāng)于是函數(shù)功能的擴(kuò)展。
裝飾器本質(zhì)上也是Python的函數(shù),其他函數(shù)在不需要做任何代碼變動(dòng)的前提下增加額外功能,裝飾器的返回值也是一個(gè)函數(shù)對(duì)象。
2. 裝飾器應(yīng)用場(chǎng)景
切面需求的場(chǎng)景,
比如:插入日志、性能測(cè)試、事務(wù)處理、緩存、權(quán)限校驗(yàn)等場(chǎng)景。
有了裝飾器,就可以抽離出大量與函數(shù)功能本身無(wú)關(guān)的雷同代碼并繼續(xù)重用。
進(jìn)一步說(shuō):裝飾器的作用就是為已經(jīng)存在的對(duì)象添加額外的功能。
3. 裝飾器背景
首先看一個(gè)函數(shù)
def driving():
print("i'm driving")
現(xiàn)在有一個(gè)新的需求,希望可以記錄下函數(shù)的執(zhí)行日志,于是在代碼中添加日志代碼:
def driving():
print("i'm driving")
logging.info("driving is running")
假設(shè)stoping , parking 都有類似的需求。難道也把每個(gè)logging函數(shù)寫道函數(shù)內(nèi)部?這樣就會(huì)造成大量的冗余代碼。如何必免這樣的情況產(chǎn)生?
重新定義一個(gè)函數(shù):專門處理日志 ,日志處理完之后再執(zhí)行真正的業(yè)務(wù)代碼。
def record_logging(func):
logging.info("%s is running" % func.__name__)
func()
def driving():
print("i'm driving")
record_logging(driving):
邏輯上很清晰, 但是如果這樣做,每次都要將一個(gè)函數(shù)作為參數(shù)傳遞給record_logging函數(shù)。并且這樣做破壞原來(lái)的函數(shù)結(jié)構(gòu)。之前運(yùn)行的driving 函數(shù),現(xiàn)在需要運(yùn)行 record_logging函數(shù)。
如何在不破壞原來(lái)的函數(shù)基礎(chǔ),實(shí)現(xiàn)這樣的功能? 答案就是裝飾器的使用。
4. 裝飾器使用
def record_logging(func):
def wrapper(*args, **kwargs):
logging.info("%s is running" % func.__name__)
return func(*args, **kwargs)
return wrapper
def driving():
print("i'm driving")
driving = record_logging(driving)
driving ()
函數(shù)record_logging 就是裝飾器,真正執(zhí)行的業(yè)務(wù)函數(shù)driving 被包含在里面了,看上去 driving 被函數(shù)record_logging 裝飾了。
@符號(hào)是裝飾器的語(yǔ)法糖,在定義函數(shù)的時(shí)候使用,避免再一次賦值操。
def record_logging(func):
def wrapper(*args, **kwargs):
logging.info("%s is running" % func.__name__)
return func(*args, **kwargs)
return wrapper
@record_logging
def driving():
print("i'm driving")
@record_logging
def stoping():
print("i'm stoping")
driving()
stoping()
加入@ 語(yǔ)法糖之后,就能省略driving = record_logging(driving)
這一步驟。如果有其它類似的函數(shù)調(diào)用就可以直接用裝飾器。提高了程序的可重復(fù)利用性,并增加了程序的可讀性。