本篇內容主要講解“怎么用Python編寫一個裝飾器”,感興趣的朋友不妨來看看。本文介紹的方法操作簡單快捷,實用性強。下面就讓小編來帶大家學習“怎么用Python編寫一個裝飾器”吧!
首先概念,裝飾器是閉包的一種應用,需要滿足一下規則:
1.在不更改原功能函數的內部代碼,并且改變調用方法的情況下為原函數增加新功能
2.遵循開放封閉原則,什么是開放封閉原則呢?
a.已實現的功能可以添加或擴展新的功能(開放原則)
b.不修改已實現功能的內部代碼(封閉原則)
其次作用,登錄驗證、函數運行時長統計、執行函數之前做的準備工作,執行函數之后清理功能,總之你能想到的擴展功能大部分都可以實現,而且是在原功能代碼不做修改的情況下就可以優雅的完成!
一起來看下裝飾器的實踐,親身經歷的問題!因為工作當中經常性需要獲取接口數據,所以就簡單的封裝了一個獲取數據的方法,代碼如下:
import requests import re def send_request_by(method, url, data): """ 請求接口獲取數據 :param method: 發起請求的方式 :param url: 請求地址 :param data: 請求數據 :return: """ if re.match("POST", method, flags=re.IGNORECASE): response = requests.post(url, data=data) if re.match("GET", method, flags=re.IGNORECASE): response = requests.get(url, data=data) return response
目前看來對自己的需求已經滿足,但是每次請求的時候發現還是報錯!最終通過抓包工具分析發現,在客戶端進行接口調用的時都多了一個"sign"字段,這個字段是怎么來的呢?經過分析"sign"是在加密后得來的,為了解決這個問題對代碼進行了一次改造,代碼如下:
def send_request_by(method, url, data): md5_pwd = MD5Password() data['sign'] = md5_pwd(data) if re.match("POST", method, flags=re.IGNORECASE): response = requests.post(url, data=data) if re.match("GET", method, flags=re.IGNORECASE): response = requests.get(url, data=data) return response
這樣看起來是解決了問題,但其實沒有靈活的解決問題。試問,如果哪天又不需要加密簽名是不是還得把新加的兩行代碼干掉?那怎么能不修改原功能又能添加加密的功能呢。經過和大佬討教之后,發現可以通過裝飾器來實現,再一次對代碼進行了改造,代碼如下:
def sign_md5(func): def wrapper(*args, **kwargs): if not kwargs.get('data'): raise KeyError("not found Key 'data'") if kwargs.get('data') is None: raise ValueError(f'{kwargs.get("data")} of value is None') # 首字母排序 sort_data = json.loads(json.dumps(kwargs.get('data'), sort_keys=True)) # 私有加密規則,生成簽名 md5_pwd = MD5Password() sign = md5_pwd(sort_data) sort_data['sig'] = sign kwargs['data'] = sort_data ret = func(*args, **kwargs) return ret return wrapper @sign_md5 # send_request_by = sign_md5(send_request_by) def send_request_by(method, url, data): # md5_pwd = MD5Password() # data['sign'] = md5_pwd(data) if re.match("POST", method, flags=re.IGNORECASE): response = requests.post(url, data=data) if re.match("GET", method, flags=re.IGNORECASE): response = requests.get(url, data=data) return response
經過測試之后發現,非常靈活的解決問題,不需要加密的時候注釋掉@sign_md5即可!那么這個@sign_md5到底做了什么?
其實質就是在沒有使用"@"魔法的情況下是sign_md5(send_request_by)(method, ulr, data),而當使用"@"魔法進行裝飾后,代碼執行到此行時解析器會將被裝飾的send_request_by作為一個參數傳遞給sign_md5,即send_request_by這個函數已經作為變量傳遞給了sign_md(func)中的func參數,并返回了wrapper這個函數,在接下來調用send_request_by(method, url, data)這個函數,其實此時send_request_by已經不是原先的def send_request_by(method, url, data)中的send_request_by,而是指向了wrapper(*args, **kwargs)這個函數。wrapper這個函數接收任意參數,所以當執行send_request_by(method, url, data)函數時,其實把method, url, data參數傳遞給warpper函數,并執行wrapper內部代碼,而wrapper函數內部的func就是我們傳入的send_request_by函數了,意思可以理解為,我們將參數傳給了wrapper函數,在wrapper函數內將請求參數進行加密后,傳遞給了send_request_by函數進行請求,從而在請求之前完成了加密的過程。而wrapper函數內func(*args, **kwargs)指向的是send_request_by(method, url, data)而ret也就是send_request_by(method, url, data)函數的返回結果,因此將ret返回出來。這塊比較繞口,多捋一捋相信憑你的聰明才智一定會明白!到時你一定會認為,哇!竟然如此簡單?。?!而到此為止,最簡單的無參數裝飾器就此完成!領導一定會夸你,秀兒!
那么,在解決了這個需求之后自己又有了新的疑惑,如果哪天開發不按照字段首字母排序了,我該怎么辦????是不是又要重新寫裝飾器了???那有什么辦法能在裝飾器內控制是否排序呢?那么也是經過各種腦補,又又又進行了一次代碼的修改,代碼如下:
def sign_sort(sort=True): def sign_md5(func): def wrapper(*args, **kwargs): if not kwargs.get('data'): raise KeyError("not found Key 'data'") if kwargs.get('data') is None: raise ValueError(f'{kwargs.get("data")} of value is None') # sort 參數控制是否按首字母排序 sort_data = json.dumps(kwargs.get('data'), sort_keys=True) if sort else json.dumps(kwargs.get('data')) sort_data = json.loads(sort_data) # 私有加密規則,生成簽名 md5_pwd = MD5Password() sign = md5_pwd(sort_data) sort_data['sig'] = sign kwargs['data'] = sort_data ret = func(*args, **kwargs) return ret return wrapper return sign_md5 @sign_sort(sort=True) # send_request_by = sign_sort(sort=Ture)(send_request_by) def send_request_by(method, url, data): print(data) if re.match("POST", method, flags=re.IGNORECASE): response = requests.post(url, data=data) elif re.match("GET", method, flags=re.IGNORECASE): response = requests.get(url, data=data) return respons
那這一次是做了哪些優化呢?其實還是在學習無參裝飾器的基礎之上學習了一下帶參數裝飾器。仍然還是不變的。我們一起來分析一下,
首先來看sign_sort(sort=True),sign_sort實質是一個函數接收一個參數,并返回sign_md5函數。
當代碼執行到"@"所在行時,同樣會把被裝飾send_request_by函數作為一個參數傳遞給sign_sort(sort=True)函數的調用結果(即sign_md5)。
也就是說send_request_by函數又是作為一個變量(函數也可以是變量)傳遞給了sign_md5函數中的func參數,并返回了一個wrapper函數。
在后面調用send_request_by(method, url, data)函數時,同樣此時的send_request_by已經不再是def send_request_by(method, url, data)函數中的send_request_by,而是wrapper(*args, **kwargs)函數。
wrapper函數接收任意參數,所以當執行send_request_by(method, url, data)時,會把method, url, data傳遞給wrapper函數,執行wrapper函數內的代碼,而wrapper內部的func(*args, **kwargs)還是指向send_request_by,可以理解為通過wrapper函數將參數傳遞給send_request_by函數,而在傳遞給send_request_by之前,我們可以對參數做任意的操作,因此我在傳遞之前判斷了sort參數是否為True作為排序的開關,再對請求數據data進行加密的操作,最后帶著加密簽名傳遞給send_request_by函數進行發送請求。
到此,相信大家對“怎么用Python編寫一個裝飾器”有了更深的了解,不妨來實際操作一番吧!這里是億速云網站,更多相關內容可以進入相關頻道進行查詢,關注我們,繼續學習!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。