这是在微信看到的一篇文章,伙伴刚学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小白眼中的装饰器相关推荐

  1. 51自学网python初学者教程-超适合小白的python新手教程

    python介绍 这是我们专门为 小白 量身打造的Python新手教程,具有如下特点: 全视频,手把手,零起点,项目实例,基于船新的Python 版本. Python是一种计算机程序设计语言.你可能已 ...

  2. 为何人工智能首推Python 初学者怎么学Python

    为何人工智能首推Python?初学者怎么学Python?我们知道,近两年人工智能发展的速度呈指数型增长,各行各业都在自己的产品上面加个AI.人工智能时代即将来临,人工智能是大势所趋.越来越多的人意识到 ...

  3. python初学者怎么入门-python初学者怎么入门

    据百度大数据统计每月有4260705的用户在百度检索Python相关介绍,在大众周围,也总是充斥着各种如何学习Python的声音,许多人已经对于是否"应该学习Python"也都发表 ...

  4. 1024,一封写给CSDN家园Python初学者的信 | Python初级、中级、高级学习路线

    又是一年1024,祝所有程序员节日快乐,健康开心,祝CSDN越来越好.转眼,已经在CSDN分享了十多年博客,感谢大家的陪伴和祝福,在这里我与许多人成为了朋友,感恩.非常遗憾,这次没能去长沙岳麓书院见很 ...

  5. 1024,一封写给CSDN家园Python初学者的信 Python初级、中级、高级学习路线

    又是一年1024,祝所有程序员节日快乐,健康开心,祝CSDN越来越好.转眼,已经在CSDN分享了十多年博客,感谢大家的陪伴和祝福,在这里我与许多人成为了朋友,感恩.非常遗憾,这次没能去长沙岳麓书院见很 ...

  6. 初学者python用哪个版本好-什么是Python?初学者应该学python哪个版本?

    什么是Python?(这需要说明的是,Python并不是以蛇命名,而是以电视节目MontyPythonsFlyingCiret.来命名的),标志如图1.1所示・它是19年由荷兰人GmdvanRossu ...

  7. python初学者_xx分钟python初学者财务分析

    python初学者 项目背景 (Project Background) Besides working to earn an income, investing is the best way to ...

  8. Python基础day4 函数对象、生成器 、装饰器、迭代器、闭包函数

    一.函数对象 正确理解 Python函数,能够帮助我们更好地理解 Python 装饰器.匿名函数(lambda).函数式编程等高阶技术. 函数(Function)作为程序语言中不可或缺的一部分,太稀松 ...

  9. python 递归函数_让你Python到很爽的加速递归函数的装饰器

    Python技巧--好用的一个装饰器 今天我们会讲到一个装饰器. 注记:链接"装饰器"指向廖雪峰老师的Python3教程中的装饰器教程.可以在这里快速了解什么是装饰器. `@fun ...

最新文章

  1. android 创建3个按钮,【记录】继续尝试给Android程序的右上角的ActionBar中添加三个点的选项按钮...
  2. Linux快速查看某条命令的版本和存放的位置(ls -l `which mvn`)
  3. 10.24T3 解方程 取模意义下运算+秦九韶算法
  4. 深入理解Oracle字符串函数Translate()
  5. 武侠乂服务器位置在哪,武侠乂手游秘境在哪里 地图秘境宝藏分布位置大全
  6. Java输入输出流和文件操作
  7. 归并排序——算法系列
  8. tc.html是什么页面,HTML iframe属性详细说明
  9. NoSQL数据库探讨- 为什么要用非关系数据库?
  10. VC文件读写操作总结
  11. keil MDK uVision 5最新版本下载(含有注册机)
  12. appuim + python 实现 趣头条 自动阅读
  13. bzoj1984 月下“毛景树”
  14. Revisiting RCNN: On Awakening the Classification Power of Faster RCNN解读
  15. 网页效果图设计之色彩配色索引
  16. 微软漏洞导致SQL注入威胁
  17. 成长,没你想象的那么迫切!
  18. 华为三层交换机5700系列配置示例
  19. 基于WEB的多媒体素材管理库的开发与应用,免费分享
  20. 硕士毕业论文模板(专业硕士)

热门文章

  1. 【译】开发大型 Angular 应用的12条架构清单
  2. 20145234黄斐《网络对抗技术》实验八、Web基础
  3. 《Cocos2d 跨平台游戏开发指南(第2版)》一1.9 添加动作到精灵
  4. C#中string a=null和string b=区别
  5. 终于弄明白 i = i++和 i = ++i 的区别了!
  6. Java 实现单例模式的 9 种方法
  7. 脑洞大开,如何生成 2018 年度代码报告
  8. 这样学习正则表达式就轻松了!
  9. Java的时间为何从1970年1月1日开始
  10. RESTful Web 服务 - 安全性