如果你還不知道什么是裝飾器,請看這里,
請讀者不要感到文章的冗長無聊,我會盡量用生動的表達,但我保證這是你見過最詳細的教程(之一),如果你看完不能理解,歡迎在評論區(qū)批評我
正如之前所說,裝飾器就是函數(shù),那么是函數(shù)就肯定可以傳參數(shù).
其實裝飾器在調(diào)用時本身就把它所裝飾的函數(shù)作為參數(shù)傳給了它,只是沒有很明白的寫出來而已,而下面我要介紹的是"看得見的參數(shù)傳遞過程".
現(xiàn)在假設老板需要一個功能,要把一個函數(shù)結(jié)果(可能是爬蟲獲取的數(shù)據(jù))發(fā)送給某個客戶.你很自然的想到用裝飾器來實現(xiàn),但是有個問題,老板的客戶很多,他隨時可能更換發(fā)送的客戶,這也就意味著你不能把客戶給"寫定",那么把客戶作為裝飾器的參數(shù)就顯得十分必要了.
(我把與裝飾器無關(guān)的功能用文字來代替了)
你想象中代碼樣子和功能
@sendMsgTo("老王")def spider(): return "爬到的數(shù)據(jù)"spider()
out:
已把 爬到的數(shù)據(jù) 發(fā)送給老王
@sendMsgTo("老胡")def spider(): return "爬到的數(shù)據(jù)"spider()
out:
已把 爬到的數(shù)據(jù) 發(fā)送給老王
之前介紹過,當函數(shù)名后面跟上括號后代表執(zhí)行該函數(shù)的意思,而普通裝飾器的語法是不帶括號的(可以理解為:就是把這個函數(shù)拿來裝飾,而不是這個函數(shù)的執(zhí)行結(jié)果)
既然我們知道了@
符后面跟的是函數(shù)(名),而不是函數(shù)的執(zhí)行結(jié)果,那么我們該怎么實現(xiàn)上面例子的效果呢?
很簡單:只要使上面例子中的sendMsgTo(XX)
返回一個符合裝飾器語法的函數(shù)就可以啦!
符合裝飾器語法的函數(shù)長這樣:
def decorator(func): def wrapper(*args, **kwargs): func(*args, **kwargs) ''' 一些其他操作 ''' return wrapper
如果要返回上面的這個函數(shù),那就必須這么寫:
def returnDecorator(): def decorator(func): def wrapper(*args, **kwargs): func(*args, **kwargs) ''' 一些其他操作 ''' return wrapper return decorator
就是加個一頭一尾的事情,當然,最外層函數(shù)的參數(shù),變量等等內(nèi)層的函數(shù)都是可以使用的
有了上面的理解,那么我們來完善最開始我們需要的裝飾器吧!
def sendMsgTo(name): # 定義外層函數(shù),可以接收額外參數(shù) def decorator(func): # 定義內(nèi)存函數(shù)實際上就是裝飾器 def wrapper(*args, **kwargs): data = func(*args, **kwargs) print("以把 {} 發(fā)送給{}".format(data, name)) # 調(diào)用外層函數(shù)傳進來的參數(shù) return wrapper return decorator # 返回這個裝飾器@sendMsgTo("老王")def spider(): return "爬到的數(shù)據(jù)"if __name__ == '__main__': spider()
out:
以把 爬到的數(shù)據(jù) 發(fā)送給老王
分析:
@sendMsgTo("老王")
這句話執(zhí)行了sendMsgTo
這個函數(shù),并把老王
作為參數(shù).sendMsgTo
的作用實際上就是定義了一個裝飾器函decorator
并把該裝飾器返回.name
)sendMsgTo
并不是真正的裝飾器,他只是負責接收外界的參數(shù)來構(gòu)造真正的裝飾器并返回.因為真正的裝飾器函數(shù)是只能有一個參數(shù),那就是被裝飾的函數(shù)在這之前,我們都是通過函數(shù)來實現(xiàn)裝飾器的功能,從現(xiàn)在起教大家使用類來實現(xiàn)裝飾器的功能,這樣一來就可以添加更多的功能(雖然絕大多數(shù)情況下使用函數(shù)裝飾器也可以做,但是代碼的可讀性不高,因為所有功能都集中在一個函數(shù)中,使用類裝飾器就可以把功能單獨作為類的方法,使用時調(diào)用方法即可)
class AAA(object): def __call__(self, func): def wrapper(): print(func()) print("這是類裝飾器給你的附加功能") return wrapper@AAA()def spider(): return "爬到的數(shù)據(jù)"if __name__ == '__main__': spider()
out:
爬到的數(shù)據(jù)這是類裝飾器給你的附加功能
注意:
使用類來實現(xiàn)裝飾器必須實現(xiàn)__call__
方法(其實也不是強制)
調(diào)用類裝飾器時要加()
,這不是調(diào)用函數(shù)的意思,這是實例化對象的意思
實例化對象后Python會自動調(diào)用__call__
方法作為裝飾器(本質(zhì)還是函數(shù)),如果你不實現(xiàn)__call__
方法那么就需要你自己手動調(diào)用了,舉個例子:
class AAA(object): def myDec(self, func): def wrapper(): print(func()) print("這是類裝飾器給你的附加功能") return wrapperaaa = AAA()@aaa.myDecdef spider(): return "爬到的數(shù)據(jù)"if __name__ == '__main__': spider()
及其不推薦這種寫法,一個字繁
通過上面的介紹,我相信你們都理解了怎么使用類來寫一個裝飾器(其實本質(zhì)還是函數(shù)),那么我們就在最開始的例子上加點要求.
附加要求:現(xiàn)在老板覺得直接把數(shù)據(jù)發(fā)給客戶不太穩(wěn)重,他想先檢查一下數(shù)據(jù)是否存在敏感信息,將其去除后再發(fā)送
使用類裝飾器來完成:
class sendMsgTo(object): # 函數(shù)參數(shù) def __init__(self, name): self.name = name def __call__(self, func): def wrapper(*args, **kwargs): data = func(*args, **kwargs) data = self.washData(data) print("以把 {} 發(fā)送給{}".format(data, self.name)) return wrapper def washData(self, data): ''' 負責清洗數(shù)據(jù),去除敏感信息 這里把 `數(shù)據(jù)` 兩個字去除換成 `東西` ''' return data.replace("數(shù)據(jù)", "東西")@sendMsgTo("老王")def spider(): return "爬到的數(shù)據(jù)"if __name__ == '__main__': spider()
out:
以把 爬到的東西 發(fā)送給老王
為了加深讀者對類裝飾器的理解,我希望讀者可以結(jié)合前面說過的內(nèi)容自己來理一遍上面代碼的執(zhí)行順序和邏輯,這并不難.
如果你看不懂歡迎評論留言,
往期精彩:深入淺出Python裝飾器,Python爬蟲之解析網(wǎng)頁,PyMySQL學習筆記
來源:http://www.icode9.com/content-1-250601.html聯(lián)系客服