装饰器学习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" />