一位Python初学者的自白:Python小白眼中的装饰器
这是在微信看到的一篇文章,伙伴刚学Python不久,我认真看了一下,确实是很真实的刚入门Python学习的伙伴!我把这篇文章拿到这来,也希望更多刚接触Python的伙伴能有所学,有所悟!
原文如下:
hello,大家好,首先介绍一下,我并不是一名真正意义上的程序猿。最近在学习Python这门语言,希望有个地方可以记录爬过的坑。站在研发鄙视链最底端,也是想通过一些手段来减少重复性工作来提高效率。那Python这门语言就能很好的帮我解决这些问题。今天就带你认识一下小白眼中的“装饰器”。
小白学习,概念是必须*3要学习并理解的!!那什么是装饰器,装饰器又可以做些什么呢!?
首先概念,装饰器是闭包的一种应用(闭包后面会讲到,本次不做阐述),需要满足一下规则:
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'] = signkwargs['data'] = sort_dataret = func(*args, **kwargs)return retreturn 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'] = signkwargs['data'] = sort_dataret = func(*args, **kwargs)return retreturn wrapperreturn 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当中的“魔法”还有很多很多,比如说上面代码中,md5_pwd = MD5Password明明是一个对象,为什么还能够像函数(md5_pwd())一样调用,以及如何写一个类装饰器呢?这些功能都是通过Python底层的魔术__call__方法来实现的!那么下一次的主题,就是__call__方法,会讲述我对__call__方法的初识,以及重新认识!
最后,文章中肯定有讲述不对的地方,欢迎各种代码大佬来怼我,教育我,指正我!!因为我真的想学好Python!!
我的愿景是立志于做一名开发、测试一条龙的工程师!代码越写越优雅!(然后,和女朋友接私活~~~hhhh)很真实的小伙伴了啊,毕竟曾经我也有过这样的想法,哈哈哈!大家在刚接触Python的时候是怎样的心情和感悟呢!也可以留言哈!
文章原创: xiaoanzi 种豆儿得瓜
转载于:https://juejin.im/post/5d1d7766f265da1bb2774e51
一位Python初学者的自白:Python小白眼中的装饰器相关推荐
- 51自学网python初学者教程-超适合小白的python新手教程
python介绍 这是我们专门为 小白 量身打造的Python新手教程,具有如下特点: 全视频,手把手,零起点,项目实例,基于船新的Python 版本. Python是一种计算机程序设计语言.你可能已 ...
- 为何人工智能首推Python 初学者怎么学Python
为何人工智能首推Python?初学者怎么学Python?我们知道,近两年人工智能发展的速度呈指数型增长,各行各业都在自己的产品上面加个AI.人工智能时代即将来临,人工智能是大势所趋.越来越多的人意识到 ...
- python初学者怎么入门-python初学者怎么入门
据百度大数据统计每月有4260705的用户在百度检索Python相关介绍,在大众周围,也总是充斥着各种如何学习Python的声音,许多人已经对于是否"应该学习Python"也都发表 ...
- 1024,一封写给CSDN家园Python初学者的信 | Python初级、中级、高级学习路线
又是一年1024,祝所有程序员节日快乐,健康开心,祝CSDN越来越好.转眼,已经在CSDN分享了十多年博客,感谢大家的陪伴和祝福,在这里我与许多人成为了朋友,感恩.非常遗憾,这次没能去长沙岳麓书院见很 ...
- 1024,一封写给CSDN家园Python初学者的信 Python初级、中级、高级学习路线
又是一年1024,祝所有程序员节日快乐,健康开心,祝CSDN越来越好.转眼,已经在CSDN分享了十多年博客,感谢大家的陪伴和祝福,在这里我与许多人成为了朋友,感恩.非常遗憾,这次没能去长沙岳麓书院见很 ...
- 初学者python用哪个版本好-什么是Python?初学者应该学python哪个版本?
什么是Python?(这需要说明的是,Python并不是以蛇命名,而是以电视节目MontyPythonsFlyingCiret.来命名的),标志如图1.1所示・它是19年由荷兰人GmdvanRossu ...
- python初学者_xx分钟python初学者财务分析
python初学者 项目背景 (Project Background) Besides working to earn an income, investing is the best way to ...
- Python基础day4 函数对象、生成器 、装饰器、迭代器、闭包函数
一.函数对象 正确理解 Python函数,能够帮助我们更好地理解 Python 装饰器.匿名函数(lambda).函数式编程等高阶技术. 函数(Function)作为程序语言中不可或缺的一部分,太稀松 ...
- python 递归函数_让你Python到很爽的加速递归函数的装饰器
Python技巧--好用的一个装饰器 今天我们会讲到一个装饰器. 注记:链接"装饰器"指向廖雪峰老师的Python3教程中的装饰器教程.可以在这里快速了解什么是装饰器. `@fun ...
最新文章
- android 创建3个按钮,【记录】继续尝试给Android程序的右上角的ActionBar中添加三个点的选项按钮...
- Linux快速查看某条命令的版本和存放的位置(ls -l `which mvn`)
- 10.24T3 解方程 取模意义下运算+秦九韶算法
- 深入理解Oracle字符串函数Translate()
- 武侠乂服务器位置在哪,武侠乂手游秘境在哪里 地图秘境宝藏分布位置大全
- Java输入输出流和文件操作
- 归并排序——算法系列
- tc.html是什么页面,HTML iframe属性详细说明
- NoSQL数据库探讨- 为什么要用非关系数据库?
- VC文件读写操作总结
- keil MDK uVision 5最新版本下载(含有注册机)
- appuim + python 实现 趣头条 自动阅读
- bzoj1984 月下“毛景树”
- Revisiting RCNN: On Awakening the Classification Power of Faster RCNN解读
- 网页效果图设计之色彩配色索引
- 微软漏洞导致SQL注入威胁
- 成长,没你想象的那么迫切!
- 华为三层交换机5700系列配置示例
- 基于WEB的多媒体素材管理库的开发与应用,免费分享
- 硕士毕业论文模板(专业硕士)
热门文章
- 【译】开发大型 Angular 应用的12条架构清单
- 20145234黄斐《网络对抗技术》实验八、Web基础
- 《Cocos2d 跨平台游戏开发指南(第2版)》一1.9 添加动作到精灵
- C#中string a=null和string b=区别
- 终于弄明白 i = i++和 i = ++i 的区别了!
- Java 实现单例模式的 9 种方法
- 脑洞大开,如何生成 2018 年度代码报告
- 这样学习正则表达式就轻松了!
- Java的时间为何从1970年1月1日开始
- RESTful Web 服务 - 安全性