怎么用Python编写一个装饰器-创新互联
本篇内容主要讲解“怎么用Python编写一个装饰器”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“怎么用Python编写一个装饰器”吧!
创新互联客户idc服务中心,提供德阳机房服务器托管、成都服务器、成都主机托管、成都双线服务器等业务的一站式服务。通过各地的服务中心,我们向成都用户提供优质廉价的产品以及开放、透明、稳定、高性价比的服务,资深网络工程师在机房提供7*24小时标准级技术保障。首先概念,装饰器是闭包的一种应用,需要满足一下规则:
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编写一个装饰器”有了更深的了解,不妨来实际操作一番吧!这里是创新互联网站,更多相关内容可以进入相关频道进行查询,关注我们,继续学习!
网站题目:怎么用Python编写一个装饰器-创新互联
标题来源:http://hbruida.cn/article/dpisoi.html