Python裝飾器使用你可能不知道的幾種姿勢(shì)
前言
在Python中,裝飾器是一種十分強(qiáng)大并且好用的語(yǔ)法,一些重復(fù)的代碼使用裝飾器語(yǔ)法的話能夠使代碼更容易理解及閱讀。
因此在這里簡(jiǎn)單總結(jié)了一下Python中裝飾器的幾種用法以及需要注意的事情。
一、在裝飾器中獲取被裝飾函數(shù)的參數(shù)
假設(shè)我們?cè)陂_(kāi)發(fā)web的時(shí)候,需要做反爬。要判斷接口的訪問(wèn)來(lái)源我們就可以通過(guò)下面裝飾器的方法來(lái)實(shí)現(xiàn):
def mydecorator(func): def wrapped(*args, **kwargs): print("進(jìn)入裝飾器") if args[0]['header'] == 'spider': print("code: 400") return result = func(*args, **kwargs) return result return wrapped @mydecorator def request_page(request): print("一個(gè)訪問(wèn)請(qǐng)求") print("返回了response") if __name__ == '__main__': request = { 'data': 100, 'header': 'spider' } request_page(request)
在這個(gè)裝飾器中,我們?cè)谘b飾器中獲取了request中的header參數(shù),如果判斷訪問(wèn)來(lái)源于爬蟲,那么便給它返回一個(gè)400。
使用裝飾器的寫法等同于下面不使用裝飾器的寫法
def mydecorator(*args, **kwargs): print("進(jìn)入函數(shù)") if args[0]['header'] == 'spider': print("code: 400") return False return True def request_page(request): if not mydecorator(request): return print("訪問(wèn)一個(gè)網(wǎng)頁(yè)") print("得到了response") if __name__ == '__main__': request = { 'data': 100, 'header': 'spider' } request_page(request)
在只需要裝飾一個(gè)函數(shù)的時(shí)候后面一種寫法可能更優(yōu)于裝飾器的寫法,但是在需要裝飾很多個(gè)函數(shù)的時(shí)候,使用裝飾器明顯是更好的選擇。
二、在裝飾器獲取函數(shù)的返回值
有的時(shí)候我們需要對(duì)函數(shù)的返回值做出判斷,但又不想直接將判斷寫在函數(shù)里的時(shí)候,我們也可以使用裝飾器來(lái)實(shí)現(xiàn):
def mydecorator(func): def wrapped(*args, **kwargs): print("進(jìn)入裝飾器") result = func(*args, **kwargs) if result == 400: print("response is 400!") return False return True return wrapped @mydecorator def request_page(): print("訪問(wèn)一個(gè)網(wǎng)頁(yè)") print("得到了response") return 200 if __name__ == '__main__': print(request_page())
三、給裝飾器傳入?yún)?shù)
在實(shí)際應(yīng)用中,我們有時(shí)需要根據(jù)函數(shù)的執(zhí)行狀態(tài)來(lái)重復(fù)執(zhí)行。例如在編寫爬蟲的時(shí)候,可能由于網(wǎng)絡(luò)的原因會(huì)導(dǎo)致一些頁(yè)面訪問(wèn)失敗,這時(shí)我們就需要根據(jù)爬蟲的返回結(jié)果進(jìn)行重復(fù)請(qǐng)求。
def retry(MAXRETRY=3): def decorator(func): def wrapped(*args, **kwargs): print("進(jìn)入裝飾器") result = 0 retry = 1 while result != 200 and retry <= MAXRETRY: result = func(*args, **kwargs) print("重試第%s次" % retry) retry += 1 return result return wrapped return decorator @retry(5) def request_page(): print("訪問(wèn)一個(gè)網(wǎng)頁(yè)") print("得到了response") return 400
在這里我們假設(shè)訪問(wèn)一個(gè)網(wǎng)頁(yè)得到400的時(shí)候便重新請(qǐng)求。我們?cè)趓etry裝飾器里傳了一個(gè)5,這表示我們希望重試的最大次數(shù)為5次,如果不傳入這個(gè)值,那么它的默認(rèn)重試次數(shù)則為3次。
在熟悉了基本裝飾器的寫法后,傳參裝飾器的寫法也十分的好理解了。就是在外面多加了一層函數(shù),用于傳入?yún)?shù)。
四、裝飾器文檔的問(wèn)題
我們都知道通過(guò)魔術(shù)方法__doc__可以獲取我們寫在代碼中的文檔,那么你是否知道使用裝飾器后,會(huì)造成被包裝函數(shù)的文檔被裝飾器的文檔覆蓋的問(wèn)題呢。
def request_page(): ''' request_page 函數(shù)文檔 :return: ''' print("訪問(wèn)一個(gè)網(wǎng)頁(yè)") print("得到了response") if __name__ == '__main__': print(request_page.__doc__)
在上面對(duì)上面未使用裝飾的代碼使用__doc__方法的時(shí)候,我們得到的結(jié)果是:
In[3]: request_page.__doc__ Out[3]: '\n request_page 函數(shù)文檔\n :return:\n '
這是我們理想中的結(jié)果!
但是當(dāng)我們將上述函數(shù)使用裝飾器裝飾后:
def decorator(func): def wrapped(*args, **kwargs): ''' 裝飾器文檔 :param args: :param kwargs: :return: ''' print("進(jìn)入裝飾器") result = func(*args, **kwargs) return result return wrapped @decorator def request_page(): ''' request_page 函數(shù)文檔 :return: ''' print("訪問(wèn)一個(gè)網(wǎng)頁(yè)") print("得到了response")
我們?cè)僖淮芜\(yùn)行__doc__魔術(shù)方法的時(shí)候,得到的結(jié)果卻是裝飾器的內(nèi)部文檔:
In[4]: request_page.__doc__ Out[4]: '\n 裝飾器文檔\n :param args:\n :param kwargs:\n :return:\n ' In[5]: request_page.__name__ Out[5]: 'wrapped'
這個(gè)問(wèn)題會(huì)使得我們的調(diào)試變得困難,也會(huì)使許多自動(dòng)文檔生成工具失去效果。
解決這個(gè)問(wèn)題的最好辦法就是使用 functools包的wraps()模塊來(lái)將裝飾器進(jìn)行一個(gè)包裝。
from functools import wraps def decorator(func): @wraps(func) def wrapped(*args, **kwargs): ''' 裝飾器 :param args: :param kwargs: :return: ''' print("進(jìn)入裝飾器") result = func(*args, **kwargs) return result return wrapped @decorator def request_page(): ''' request_page 函數(shù)文檔 :return: ''' print("訪問(wèn)一個(gè)網(wǎng)頁(yè)") print("得到了response")
使用wraps將裝飾器裝飾后,這樣我們的函數(shù)便能夠保存它的一些重要數(shù)據(jù)了。
In[3]: request_page.__doc__ Out[3]: '\n request_page 函數(shù)文檔\n :return:\n ' In[3]: request_page.__name__ Out[4]: 'request_page'
五、使用class的寫法來(lái)編寫裝飾器
雖然大多數(shù)的裝飾器都是通過(guò)函數(shù)的寫法來(lái)實(shí)現(xiàn)的,但同樣的可以通過(guò)類的寫法來(lái)實(shí)現(xiàn)裝飾器。
使用類的寫法,我們可以實(shí)現(xiàn)一些使用函數(shù)寫法不太好實(shí)現(xiàn)的需求。例如記錄一個(gè)函數(shù)執(zhí)行的次數(shù)
class Decorator(): def __init__(self,func): print('類初始化') self.func = func self.count = 0 def __call__(self, *args, **kwargs): print('進(jìn)入裝飾器') result = self.func(*args,**kwargs) self.count += 1 return result @Decorator def request_page(): ''' request_page :return: ''' print("訪問(wèn)一個(gè)網(wǎng)頁(yè)") print("得到了response")
六、總結(jié)
裝飾器是Python里比較高級(jí)的一種語(yǔ)法,這里只是介紹了它的幾種使用技巧,以及需要注意的問(wèn)題。借用金庸先生的話,“武功無(wú)高低,修為有深淺”。想要更加靈活的使用裝飾器,深入理解它的原理,我們?cè)谄綍r(shí)還是需要加強(qiáng)基本功的學(xué)習(xí)!
好了,以上就是這篇文章的全部?jī)?nèi)容了,希望本文的內(nèi)容對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,謝謝大家對(duì)腳本之家的支持。
相關(guān)文章
使用sklearn之LabelEncoder將Label標(biāo)準(zhǔn)化的方法
今天小編就為大家分享一篇使用sklearn之LabelEncoder將Label標(biāo)準(zhǔn)化的方法,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2018-07-07python進(jìn)程間通信Queue工作過(guò)程詳解
這篇文章主要介紹了python進(jìn)程間通信Queue工作過(guò)程詳解,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2019-11-11使用python搭建Django應(yīng)用程序步驟及版本沖突問(wèn)題解決
這篇文章主要介紹了使用python搭建Django應(yīng)用程序的步驟,最近還解決了因版本沖突出現(xiàn)的錯(cuò)誤2013-11-11python 通過(guò)麥克風(fēng)錄音 生成wav文件的方法
今天小編就為大家分享一篇python 通過(guò)麥克風(fēng)錄音 生成wav文件的方法,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2019-01-01Python中數(shù)字(Number)數(shù)據(jù)類型常用操作
本文主要介紹了Python中數(shù)字(Number)數(shù)據(jù)類型常用操作,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2023-02-02Python入門教程(二十)Python的Lambda表達(dá)式
這篇文章主要介紹了Python入門教程(二十)Python的Lambda表達(dá)式,lambda表達(dá)式是一行的函數(shù)。它們?cè)谄渌Z(yǔ)言中也被稱為匿名函數(shù),lambda表達(dá)式非常有用,可以讓代碼簡(jiǎn)單,簡(jiǎn)潔,需要的朋友可以參考下2023-04-04