装饰器学习1
时间:2022-07-22
本文章向大家介绍装饰器学习1,主要内容包括其使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。
需求: 一个加法函数,想增强它的功能,能够输出被调用过以及调用的参数信息
<img class="alignnone size-full wp-image-648 " src="https://www.devilf.cc/wp-content/uploads/2019/02/img_5c6cb7f422920.png" alt="" srcset="https://www.devilf.cc/wp-content/uploads/2019/02/img_5c6cb7f422920.png 402w, https://www.devilf.cc/wp-content/uploads/2019/02/img_5c6cb7f422920-150x25.png 150w, https://www.devilf.cc/wp-content/uploads/2019/02/img_5c6cb7f422920-300x49.png 300w" sizes="(max-width: 402px) 100vw, 402px" />
增加信息输出功能
<img class="alignnone size-full wp-image-649 " src="https://www.devilf.cc/wp-content/uploads/2019/02/img_5c6cb7f433c8c.png" alt="" srcset="https://www.devilf.cc/wp-content/uploads/2019/02/img_5c6cb7f433c8c.png 547w, https://www.devilf.cc/wp-content/uploads/2019/02/img_5c6cb7f433c8c-150x24.png 150w, https://www.devilf.cc/wp-content/uploads/2019/02/img_5c6cb7f433c8c-300x49.png 300w" sizes="(max-width: 547px) 100vw, 547px" />
上面的加法函数是完成了需求,但是有以下缺点
打印语句的耦合太高
</li>
<li>
加法函数属于业务功能,而输出信息的功能,属于非业务功能代码,不该放在业务函数加法中
</li>
进一步改进
<img class="alignnone size-full wp-image-650 " src="https://www.devilf.cc/wp-content/uploads/2019/02/img_5c6cb7f44c674.png" alt="" srcset="https://www.devilf.cc/wp-content/uploads/2019/02/img_5c6cb7f44c674.png 614w, https://www.devilf.cc/wp-content/uploads/2019/02/img_5c6cb7f44c674-150x76.png 150w, https://www.devilf.cc/wp-content/uploads/2019/02/img_5c6cb7f44c674-300x152.png 300w" sizes="(max-width: 614px) 100vw, 614px" />
做到了业务功能分离,但是fn函数调用传参是个问题
再进一步改进,解决传参问题
<img class="alignnone size-full wp-image-651 " src="https://www.devilf.cc/wp-content/uploads/2019/02/img_5c6cb7f461c41.png" alt="" srcset="https://www.devilf.cc/wp-content/uploads/2019/02/img_5c6cb7f461c41.png 616w, https://www.devilf.cc/wp-content/uploads/2019/02/img_5c6cb7f461c41-150x78.png 150w, https://www.devilf.cc/wp-content/uploads/2019/02/img_5c6cb7f461c41-300x155.png 300w" sizes="(max-width: 616px) 100vw, 616px" />
这里用到了可变参数和参数解构的方法解决了传参问题
再进一步改进,将其柯里化
<img class="alignnone size-full wp-image-652 " src="https://www.devilf.cc/wp-content/uploads/2019/02/img_5c6cb7f461daa.png" alt="" srcset="https://www.devilf.cc/wp-content/uploads/2019/02/img_5c6cb7f461daa.png 728w, https://www.devilf.cc/wp-content/uploads/2019/02/img_5c6cb7f461daa-150x74.png 150w, https://www.devilf.cc/wp-content/uploads/2019/02/img_5c6cb7f461daa-300x148.png 300w" sizes="(max-width: 728px) 100vw, 728px" />
我们这里再换一种调用方法看下效果
<img class="alignnone size-full wp-image-653 " src="https://www.devilf.cc/wp-content/uploads/2019/02/img_5c6cb7f46cc49.png" alt="" srcset="https://www.devilf.cc/wp-content/uploads/2019/02/img_5c6cb7f46cc49.png 1148w, https://www.devilf.cc/wp-content/uploads/2019/02/img_5c6cb7f46cc49-150x70.png 150w, https://www.devilf.cc/wp-content/uploads/2019/02/img_5c6cb7f46cc49-300x139.png 300w, https://www.devilf.cc/wp-content/uploads/2019/02/img_5c6cb7f46cc49-768x357.png 768w, https://www.devilf.cc/wp-content/uploads/2019/02/img_5c6cb7f46cc49-1024x475.png 1024w" sizes="(max-width: 1148px) 100vw, 1148px" />
这就是我们所说的装饰器的变形写法,下面看下真正的装饰器语法是如何写的
<img class="alignnone size-full wp-image-654 " src="https://www.devilf.cc/wp-content/uploads/2019/02/img_5c6cb7f53655c.png" alt="" srcset="https://www.devilf.cc/wp-content/uploads/2019/02/img_5c6cb7f53655c.png 707w, https://www.devilf.cc/wp-content/uploads/2019/02/img_5c6cb7f53655c-150x87.png 150w, https://www.devilf.cc/wp-content/uploads/2019/02/img_5c6cb7f53655c-300x174.png 300w" sizes="(max-width: 707px) 100vw, 707px" />
装饰器
它是一个函数
</li>
<li>
函数作为它的形参
</li>
<li>
返回值也是一个函数
</li>
<li>
可以使用@functionname的方式,简化调用
</li>
<li>
装饰器是高阶函数,但装饰器是对传入函数的功能的装饰(功能增强)
</li>
下面再看几个例子来理解下
<img class="alignnone size-full wp-image-655 " src="https://www.devilf.cc/wp-content/uploads/2019/02/img_5c6cb7f59c116.png" alt="" srcset="https://www.devilf.cc/wp-content/uploads/2019/02/img_5c6cb7f59c116.png 1140w, https://www.devilf.cc/wp-content/uploads/2019/02/img_5c6cb7f59c116-150x73.png 150w, https://www.devilf.cc/wp-content/uploads/2019/02/img_5c6cb7f59c116-300x146.png 300w, https://www.devilf.cc/wp-content/uploads/2019/02/img_5c6cb7f59c116-768x375.png 768w, https://www.devilf.cc/wp-content/uploads/2019/02/img_5c6cb7f59c116-1024x499.png 1024w" sizes="(max-width: 1140px) 100vw, 1140px" />
Python的文档
python是文档字符串
</li>
<li>
再函数语句块的第一行,且习惯是多行的文本,所以多使用三引号
</li>
<li>
惯例是首字母大写,第一行写概述,空一行,第三行写详细描述
</li>
<li>
可以使用特殊属性__doc__访问这个文档
</li>
例如:
<img class="alignnone size-full wp-image-656 " src="https://www.devilf.cc/wp-content/uploads/2019/02/img_5c6cb7f5ab887.png" alt="" srcset="https://www.devilf.cc/wp-content/uploads/2019/02/img_5c6cb7f5ab887.png 929w, https://www.devilf.cc/wp-content/uploads/2019/02/img_5c6cb7f5ab887-150x56.png 150w, https://www.devilf.cc/wp-content/uploads/2019/02/img_5c6cb7f5ab887-300x111.png 300w, https://www.devilf.cc/wp-content/uploads/2019/02/img_5c6cb7f5ab887-768x284.png 768w" sizes="(max-width: 929px) 100vw, 929px" />
__doc__访问文档
__name__访问程序的文件名
我们现在再进一步改进我们的装饰器,给我们的装饰器加上文档解释
<img class="alignnone size-full wp-image-658 " src="https://www.devilf.cc/wp-content/uploads/2019/02/img_5c6cb7f5de4da.png" alt="" srcset="https://www.devilf.cc/wp-content/uploads/2019/02/img_5c6cb7f5de4da.png 1326w, https://www.devilf.cc/wp-content/uploads/2019/02/img_5c6cb7f5de4da-150x82.png 150w, https://www.devilf.cc/wp-content/uploads/2019/02/img_5c6cb7f5de4da-300x164.png 300w, https://www.devilf.cc/wp-content/uploads/2019/02/img_5c6cb7f5de4da-768x419.png 768w, https://www.devilf.cc/wp-content/uploads/2019/02/img_5c6cb7f5de4da-1024x558.png 1024w" sizes="(max-width: 1326px) 100vw, 1326px" />
我们加完之后发现再打印的时候出现了问题,这并不是我们想要的,我们想要的是我们的业务函数的介绍,这里却打印了包装函数中内部函数的一个介绍,所以这里就有了问题,该如何解决呢?
我们先来分析下问题
@logger相当于 add=logger(add),其结果返回wrap这个函数,我们执行add.__doc__时,实际上获取的就是wrap.__doc__的这个文档属性,所以才出现了问题。
<img class="alignnone size-full wp-image-657 " src="https://www.devilf.cc/wp-content/uploads/2019/02/img_5c6cb7f5dc30d.png" alt="" srcset="https://www.devilf.cc/wp-content/uploads/2019/02/img_5c6cb7f5dc30d.png 1205w, https://www.devilf.cc/wp-content/uploads/2019/02/img_5c6cb7f5dc30d-150x95.png 150w, https://www.devilf.cc/wp-content/uploads/2019/02/img_5c6cb7f5dc30d-300x190.png 300w, https://www.devilf.cc/wp-content/uploads/2019/02/img_5c6cb7f5dc30d-768x486.png 768w, https://www.devilf.cc/wp-content/uploads/2019/02/img_5c6cb7f5dc30d-1024x648.png 1024w" sizes="(max-width: 1205px) 100vw, 1205px" />
这里我们新增加了一个函数,用来做属性拷贝,我们为什么要放在外层函数里去调用这个函数呢?我们为什么传的参数是fn和wrap呢?下面再分析下:
我们在不新增一个copy属性的函数前,在获取程序文档信息时实际上拿到的是增强函数内部函数的注释信息,也就是说,我们的这个copy函数要做的是将业务函数的属性先拷贝给内层函数,所以只要在调用文档属性前只要完成拷贝动作便可以
我们再来改进,看能否将copy_properties函数改造成装饰器
<img class="alignnone size-full wp-image-659 " src="https://www.devilf.cc/wp-content/uploads/2019/02/img_5c6cb7f6d604b.png" alt="" srcset="https://www.devilf.cc/wp-content/uploads/2019/02/img_5c6cb7f6d604b.png 1110w, https://www.devilf.cc/wp-content/uploads/2019/02/img_5c6cb7f6d604b-150x82.png 150w, https://www.devilf.cc/wp-content/uploads/2019/02/img_5c6cb7f6d604b-300x164.png 300w, https://www.devilf.cc/wp-content/uploads/2019/02/img_5c6cb7f6d604b-768x420.png 768w, https://www.devilf.cc/wp-content/uploads/2019/02/img_5c6cb7f6d604b-1024x560.png 1024w" sizes="(max-width: 1110px) 100vw, 1110px" />
这样语法上是没有问题的,但是我们需要两个参数,一个是fn,另一个是wrap,但是我们这种写法肯定是不正确的,所以我们需要一个带参数的装饰器
<img class="alignnone size-full wp-image-660 " src="https://www.devilf.cc/wp-content/uploads/2019/02/img_5c6cb7f6e36f8.png" alt="" srcset="https://www.devilf.cc/wp-content/uploads/2019/02/img_5c6cb7f6e36f8.png 1314w, https://www.devilf.cc/wp-content/uploads/2019/02/img_5c6cb7f6e36f8-150x78.png 150w, https://www.devilf.cc/wp-content/uploads/2019/02/img_5c6cb7f6e36f8-300x156.png 300w, https://www.devilf.cc/wp-content/uploads/2019/02/img_5c6cb7f6e36f8-768x400.png 768w, https://www.devilf.cc/wp-content/uploads/2019/02/img_5c6cb7f6e36f8-1024x533.png 1024w" sizes="(max-width: 1314px) 100vw, 1314px" />
@logger这个装饰器相当于 add=logger(add) ==> wrap
@copy_properties(fn)相当于 wrap=copy_properties(fn)(wrap) ==> _copy ==> wrap
<img class="alignnone size-full wp-image-661 " src="https://www.devilf.cc/wp-content/uploads/2019/02/img_5c6cb7f79f508.png" alt="" srcset="https://www.devilf.cc/wp-content/uploads/2019/02/img_5c6cb7f79f508.png 1309w, https://www.devilf.cc/wp-content/uploads/2019/02/img_5c6cb7f79f508-150x85.png 150w, https://www.devilf.cc/wp-content/uploads/2019/02/img_5c6cb7f79f508-300x170.png 300w, https://www.devilf.cc/wp-content/uploads/2019/02/img_5c6cb7f79f508-768x435.png 768w, https://www.devilf.cc/wp-content/uploads/2019/02/img_5c6cb7f79f508-1024x580.png 1024w" sizes="(max-width: 1309px) 100vw, 1309px" />
- 关于gcc、glibc和binutils模块之间的关系
- 贝叶斯过滤算法
- 強大的jQuery Chart组件-Highcharts
- vue2.0 配置 选项 属性 方法 事件 ——速查
- 亲密接触IIS 8和Web Deploy 3.0
- vue2.0 组件通信
- 在MongoDB中实现聚合函数
- 原生JS编写的照片墙效果实例演示特效
- 使用SuperWebSocket 构建实时 Web 应用
- Vue2.0的变化 ,组件模板,生命周期,循环,自定义键盘指令,过滤器
- 原生js编写的安全色拾色器
- 原生js写的贪吃蛇网页版游戏特效
- JS双月份显示联动效果,点击日期浮出消息提示
- JS兼容所有浏览器的一段加入收藏代码,设置为首页
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- Android Studio如何查看源码并调试的方法步骤
- Flutter中mixin的使用详解
- android实现圆形渐变进度条
- 如何在Android Studio下进行NDK开发
- flutter中build.gradle仓库的配置(解决外网下载速度过慢失败的问题)
- Android studio 实现随机位置画10个随机大小的五角星的代码
- 详解Android Studio实现用户登陆界面demo(xml实现)
- Flutter 实现进度条效果
- Android 侧边滑动关闭Activity的示例代码
- Flutter 系统是如何实现ExpansionPanelList的示例代码
- Flutter中如何使用WillPopScope的示例代码
- Android实现音乐播放进度条传递信息的两种方式(在service和activity中)
- Flutter 中 Dart的Mixin示例详解
- Android Studio 实现九宫格功能
- Android user版通过adb_enable开启adb 调试 不提示对话框的流程分析