溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

怎么用Python編寫一個裝飾器

發布時間:2021-11-20 16:46:01 來源:億速云 閱讀:277 作者:iii 欄目:編程語言

本篇內容主要講解“怎么用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編寫一個裝飾器”有了更深的了解,不妨來實際操作一番吧!這里是億速云網站,更多相關內容可以進入相關頻道進行查詢,關注我們,繼續學習!

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

亚洲午夜精品一区二区_中文无码日韩欧免_久久香蕉精品视频_欧美主播一区二区三区美女