什么是裝飾器饼丘?
類似于Java的注解夸浅。在python中叫做裝飾器仑最,目的是用于對(duì)已存在代碼進(jìn)行修改。這里的修改是狹義的帆喇,并非任意的修改警医,而是能夠?qū)ζ鋱?zhí)行過程進(jìn)行編譯時(shí)修改。
最簡(jiǎn)單示例
def DecoratorTest(fun):
def _T_fun_(id):
print("before call -----")
fun(id)
print("after call -----")
return _T_fun_
@DecoratorTest
def OutputX(id):
print(id)
OutputX(20)
輸出結(jié)果:
before call -----
20
after call -----
在沒有使用裝飾器之前坯钦,輸出20,使用裝飾器之后预皇,實(shí)現(xiàn)了對(duì)OutputX函數(shù)的邏輯修改,這個(gè)就是裝飾器的作用婉刀。
有什么用吟温?
雖然前一個(gè)章節(jié)中,大致說明了裝飾器的用法突颊,但很多人還是不會(huì)用鲁豪,原因在于不知道到底這個(gè)功能應(yīng)該應(yīng)用于什么場(chǎng)景。如果螺絲刀只是被描述為一頭是一字形或十字形的器具律秃,可能即使遇到一個(gè)螺絲爬橡,都不會(huì)想到螺絲刀,所以將工具與場(chǎng)景緊密關(guān)聯(lián)是很重要的棒动。裝飾器常用的就兩個(gè)場(chǎng)景:
- 構(gòu)建流程注入糙申,例如注入日志、性能分析處理船惨;
- 程序擴(kuò)展實(shí)現(xiàn)柜裸,適合在處理流程中添加具體擴(kuò)展實(shí)現(xiàn),實(shí)現(xiàn)類似回調(diào)功能粱锐;
怎么用疙挺?
流程注入
假設(shè)一個(gè)場(chǎng)景:設(shè)計(jì)了一個(gè)系統(tǒng),希望在某個(gè)場(chǎng)景下可以記錄系統(tǒng)內(nèi)函數(shù)的執(zhí)行過程信息怜浅,幫助進(jìn)行問題定位衔统,實(shí)現(xiàn)類似日志的功能。
簡(jiǎn)化原系統(tǒng)實(shí)現(xiàn)如下:
def FunA(a):
return a+20
def FunB(a, b):
return a+b
def FunC(a, b):
return FunA(a)+FunB(a, b)
print(FunC(30,40))
輸出結(jié)果為:
120
這時(shí)海雪,發(fā)現(xiàn)輸出數(shù)字與預(yù)期不相符,怎么辦舱殿?除了調(diào)試奥裸,如果希望通過日志來定位解決問題,那么修改系統(tǒng)實(shí)現(xiàn)如下:
def AFun(fun):
def CallBack(*args, **args2):
print("befor call "+getattr(fun,"__name__"))
ret = fun(*args, **args2)
if ret is not None:
print("Return:")
print(ret)
print("befor call "+getattr(fun,"__name__"))
return ret
return CallBack
@AFun
def FunA(a):
return a+20
@AFun
def FunB(a, b):
return a+b
@AFun
def FunC(a, b):
return FunA(a)+FunB(a, b)
print(FunC(30,40))
最終輸出如下:
befor call FunC
befor call FunA
Return:
50
befor call FunA
befor call FunB
Return:
70
befor call FunB
Return:
120
befor call FunC
120
通過這種方式沪袭,在不修改原代碼邏輯的基礎(chǔ)上湾宙,添加了日志執(zhí)行輸出功能樟氢,還可以對(duì)調(diào)用過程進(jìn)行時(shí)間統(tǒng)計(jì),分析系統(tǒng)的性能瓶頸侠鳄。
擴(kuò)展實(shí)現(xiàn)
假設(shè)場(chǎng)景:假設(shè)設(shè)計(jì)一個(gè)web框架埠啃,最起碼一個(gè)需求就是用戶可以擴(kuò)展定義響應(yīng)鏈接,例如響應(yīng)“/a/b"的"get"請(qǐng)求伟恶。有兩種設(shè)計(jì)方式碴开,先看正常常規(guī)設(shè)計(jì)方式:
url_processors = {}
def WebEngine(url, method):
if url in url_processors and method in url_processors[url]:
return url_processors[url][method]()
else:
return 404
def ProcessA():
print("in process A")
return "Hello A"
url_processors["/A"] = {}
url_processors["/A"]["get"] = ProcessA
print(WebEngine("/A", "get"))
print(WebEngine("/B", "get"))
執(zhí)行輸出結(jié)果如下:
in process A
Hello A
404
從上邊看到,修改響應(yīng)信息博秫,需要維護(hù)兩個(gè)地方的代碼潦牛,容易帶來不一致問題,也提升了維護(hù)成本挡育。利用裝飾器如何解決這個(gè)問題巴碗,代碼示例如下:
url_processors = {}
def WebEngine(url, method):
global url_processors
if url in url_processors and method in url_processors[url]:
return url_processors[url][method]()
else:
return 404
def DeclMethod( url="", method=""):
def DSF(fun):
global url_processors
url_processors[url] = {}
url_processors[url][method] = fun
return DSF
@DeclMethod(url="/A", method="get")
def ProcessA():
print("in process A")
return "Hello A"
print(WebEngine("/A", "get"))
print(WebEngine("/B", "get"))
從代碼上看,擴(kuò)展一個(gè)響應(yīng)即寒,只需要在函數(shù)前添加具體的擴(kuò)展裝飾聲明即可橡淆。執(zhí)行結(jié)果如下:
in process A
Hello A
404
總結(jié)
在學(xué)習(xí)過程中,需要將工具與場(chǎng)景進(jìn)行聯(lián)系母赵,在場(chǎng)景的解決實(shí)踐中逸爵,會(huì)發(fā)現(xiàn)很多其他不足,例如在上例中市咽,涉及了不定長(zhǎng)參數(shù)的處理痊银,裝飾器參數(shù)的處理。