当前位置: 首页 > news >正文

58、Python之函数高级:不定参数的函数,写出更加通用的装饰器

引言

上一篇文章中,我们见到引入了Python中的装饰器,通过一个简单的案例实现了一个初步的装饰器,但是,这个装饰器其实是有些缺陷。这一篇文章中,我们对上一篇文章中的装饰器进行一个优化升级,从而写出更加通用的装饰器。

本文的主要内容有:

1、简陋装饰器的缺陷

2、关于函数参数的更加通用的写法

3、更加规范通用的装饰器实现方式

简陋装饰器的缺陷

我们之所以需要使用装饰器,很多时候就是因为需要对很多现有功能,动态添加新的功能,实现更高级别的代码复用。

但是,装饰器是作用于函数的,其增强的目标是函数。但是,根据实际的需要,每个函数的参数及返回值可能是多种多样的。怎么能够写出一个装饰器,能够对各种函数都能进行装饰呢?

首先,回看一下上一篇文章中,关于统一添加登录功能的装饰器的写法:

# 扩展1:新增登录功能
def login(user):print(f"用户[{user}]登录成功")# 扩展2:装饰器:对传入的函数进行登录功能的动态添加
def login_wrap(func):def inner(user):login(user)func(user)return inner@login_wrap
def read(user):print(f"用户[{user}]查看系统相关信息")@login_wrap
def write(user):print(f"用户[{user}]修改系统相关信息")

这个装饰器最大的问题,显然是只能封装只有一个参数而且没有返回值的函数。如果函数有返回值或者函数参数个数不是一个,就会出现问题,比如,尝试通过@login_wrap封装下面这个函数:

def send_msg(from_user, to_user, content):print(f"{from_user}对{to_user}说:{content}")if __name__ == '__main__':send_msg('张三', '李四', '天气真好,万里无云,不远处飘着朵朵白云')

正常执行结果:

a7eb8aeace0e3e69c9270b37cc7e4423.jpeg

如果尝试使用@login_wrap进行封装:

from m1 import login_wrap@login_wrap
def send_msg(from_user, to_user, content):print(f"{from_user}对{to_user}说:{content}")if __name__ == '__main__':send_msg('张三', '李四', '天气真好,万里无云,不远处飘着朵朵白云')

执行结果:

748470e449d45b3f2507e591ebb89194.jpeg

只能接收一个参数,却传入了3个参数。

关于函数参数的更加通用的写法

只要装饰器嵌套的内部函数能够接收任意不定长参数,并且返回任意返回值即可(这里的任意,是指包装函数是什么样,装饰器装饰之后也应当保持一样)。

首先,返回值的通用化处理,是比较简单的,只需要将被包装函数的返回值进行原样返回即可(如果函数没有返回值,实际是返回None)。

由于,返回值的处理比较简单,这里就不进行代码的演示了。

比较头痛的是任意函数参数的实现。其实,我们在Python内置模块的函数定义中,总能看到这种任意函数参数的写法,比如:

d896e42d7ffa390fcf3aeeeeb05c0537.jpeg

再比如:

80e9fd03e15e4766727e0133b5f9befc.jpeg

其实,我们在前面的文章《一颗星,两颗星,满天都是小星星》中,已经介绍过*在函数定义中的写法。

我们可以定义一个这样的函数,然后看下,不同形式的参数传递,这种函数的形参写法是如何接收参数传递的。

直接看代码:

def test_args(*args, **kwargs):print(f"args: {args}")print(f"kwargs: {kwargs}")if __name__ == '__main__':test_args()test_args(1, 2, 3)test_args('张三', to='李四', msg='你好')


运行结果:

b79d42c05f02ec777ebd3bffb21a892c.jpeg

可以看到,一个*的形参args会把所有位置参数接收,以一个元组的形式进行存储;两个*的形参kwargs会把所有关键字参数进行接收,以一个字典的形式进行存储。

只需要通过:def xxxx(*args, **kwargs)这种方式,就可以让函数接收任意参数了。

更加规范通用的装饰器实现方式

任意函数的参数形式以及任意函数返回值都已经可以搞定了,那么我们就可以把前面的装饰器进行调整优化了,让它变得更加通用。

直接看代码:

# 扩展1:新增登录功能
def login(*args, **kwargs):print(f"用户[{args[0]}]登录成功")# 扩展2:装饰器:对传入的函数进行登录功能的动态添加
def login_wrap(func):def inner(*args, **kwargs):login(args[0])return func(*args, **kwargs)return inner@login_wrap
def read(user):print(f"用户[{user}]查看系统相关信息")@login_wrap
def write(user):print(f"用户[{user}]修改系统相关信息")@login_wrap
def send_msg(from_user, to_user, content):print(f"{from_user}对{to_user}说:{content}")if __name__ == '__main__':read('张三')write('李四')send_msg('张三', '李四', '天气真好,万里无云,不远处飘着朵朵白云')

执行结果:

b1cca1a7b2644a7470df3ae2f8277944.jpeg

可以看到,不同形式的参数的函数都可以统一进行装饰器增强了。

需要注意的是,*的使用:

e50ecfc1869ed45890d4f7ccd483062a.jpeg

对*的使用,不清楚的,可以翻一下之前的文章,也可以自行搜索引擎检索。

总结

本文首先说明了之前比较粗糙的装饰器实现的缺陷,由于参数形式、返回值等的写法大大降低了装饰器的通用性的问题;然后,回顾了接收任意参数的函数的定义;最后,基于接收任意参数的函数的写法最终优化了装饰器的实现,从而让装饰器变得更加通用。

感谢您的拨冗阅读,希望对您有所帮助!

9a51ace0fb4eba6c505fd848fa4234e5.jpeg

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • 超声波的应用
  • AOP和注解的配合使用(封装通用日志处理类)
  • 2 html5 浏览器已经支持的新API
  • 腾讯云技术深度解析:AI代码助手与微服务架构的实践应用
  • 服务器数据恢复—如何应对双循环RAID5阵列的数据丢失问题?
  • 【初出江湖】分布式之什么是分布式存储?
  • P-Tuning v2:一种普遍有效的提示调整方法
  • 三分钟搭建线上RAG应用,实现定制化的知识库问答
  • 解锁企业微信营销新纪元:智驭未来,让每一次触达都精准高效!
  • Tensorflow实现深度学习8:猫狗识别
  • Qt Dialog退出事件
  • AIGC时代从新手到高手:B端竞品分析实战案例与技巧分享
  • 华为Huawei路由器交换机SSH配置
  • 设计模式-结构型模式-组合模式
  • 学习WebGl基础知识(二)
  • -------------------- 第二讲-------- 第一节------在此给出链表的基本操作
  • $translatePartialLoader加载失败及解决方式
  • 【跃迁之路】【733天】程序员高效学习方法论探索系列(实验阶段490-2019.2.23)...
  • create-react-app做的留言板
  • FastReport在线报表设计器工作原理
  • HTTP请求重发
  • Invalidate和postInvalidate的区别
  • java8 Stream Pipelines 浅析
  • Java新版本的开发已正式进入轨道,版本号18.3
  • js算法-归并排序(merge_sort)
  • node和express搭建代理服务器(源码)
  • SpiderData 2019年2月16日 DApp数据排行榜
  • 阿里云爬虫风险管理产品商业化,为云端流量保驾护航
  • 高度不固定时垂直居中
  • 基于HAProxy的高性能缓存服务器nuster
  • 买一台 iPhone X,还是创建一家未来的独角兽?
  • 扑朔迷离的属性和特性【彻底弄清】
  • 前端性能优化--懒加载和预加载
  • 使用iElevator.js模拟segmentfault的文章标题导航
  • 函数计算新功能-----支持C#函数
  • #define MODIFY_REG(REG, CLEARMASK, SETMASK)
  • #常见电池型号介绍 常见电池尺寸是多少【详解】
  • #免费 苹果M系芯片Macbook电脑MacOS使用Bash脚本写入(读写)NTFS硬盘教程
  • #我与Java虚拟机的故事#连载01:人在JVM,身不由己
  • #我与Java虚拟机的故事#连载17:我的Java技术水平有了一个本质的提升
  • #我与Java虚拟机的故事#连载18:JAVA成长之路
  • (java)关于Thread的挂起和恢复
  • (Mac上)使用Python进行matplotlib 画图时,中文显示不出来
  • (php伪随机数生成)[GWCTF 2019]枯燥的抽奖
  • (ZT)出版业改革:该死的死,该生的生
  • (附源码)spring boot建达集团公司平台 毕业设计 141538
  • (附源码)springboot电竞专题网站 毕业设计 641314
  • (附源码)ssm高校运动会管理系统 毕业设计 020419
  • (十)【Jmeter】线程(Threads(Users))之jp@gc - Stepping Thread Group (deprecated)
  • (四)事件系统
  • (一)Dubbo快速入门、介绍、使用
  • (幽默漫画)有个程序员老公,是怎样的体验?
  • (杂交版)植物大战僵尸
  • (转)3D模板阴影原理
  • (转)创业的注意事项