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