首页 > 编程语言 >Python之带参装饰器(12)

Python之带参装饰器(12)

时间:2023-04-16 10:31:59浏览次数:36  
标签:__ 12 之带 Python wrapper add .__ copy fn

一、文档字符串

无参装饰器和带参装饰器有什么区别呢?我们先来看文档字符串

文档字符串是什么东西呢?

文档字符串.
	●Python文档字符串Documentation Strings
	●在函数(类、模块)语句块的第一行,且习惯是多行的文本,所以多使用三引号
	●文档字符串也算是合法的一条语句
	●惯例是首字母大写,第一行写概述,空一行,第三行写详细描述
	●可以使用特殊属性__doc__ 访问这个文档

文档所在的对象上有一个特殊的属性叫做__name__,特殊属性就带下划线,还有特殊属性doc。自己也可以创建

它的用法如下

def add(x,y):
	"""add function
 		x int
 		y int
		return int 
  """   
  
  通过文档字符串说明了add函数的参数是什么类型,返回值是什么类型
  
  print(add.__name__,add.__doc__)	
  
  通过调用add属性__doc__可以看到三引号里面文档
  使用print(help(add)) 也可以输出文档

我们来写一个装饰器用来,记录函数执行时间的

import datetime
import time

def logger(fn):
    def wrapper(*args, **kwargs):        
        ''' wrapper function+++++++'''
        start=datetime.datetime.now()
        
        ret = fn(*args, **kwargs)      
        
        delta=(datetime.datetime.now()-start).total_seconds()
        print("Function {} took {:.2f}s".format(fn.__name__,delta))
        
        return ret
    return wrapper             #delta 输出多少秒


@logger  # add = logger(add)
def add(x, y):
    '''add function~~~~~~ '''
    time.sleep(2)
    return x + y


我们对里的装饰器加字符串文档

print(add.__name__,add.__doc__)
这里打印会发现输出了:
	wrapper  wrapper function+++++++
    发现并没有输出add函数中的字符串文档,有点露馅了,我们这种装饰,只是
    伪装的像add,名字是一样了。但是我们知道add = logger(add)
     add=wrapper add函数指向了wrpper函数的地址空间了。wrpper标识符虽然
    已经消亡了,但是它所指向的空间,被add标识符记住了。

现在问题是,我仅仅是标识符伪装了,都是add,但是内部还没有进行处理?该怎么处
	理就是你现在能不能把这个warpper伪装了像真正的add函数一样呢?
    名字和文档字符串都应该显示的是add函数的内容

简单的总结一下我们的问题是什么

print(add.__name__,add.__doc__)	

	输出了
		wrapper  wrapper function
		只是标识符用了add,文档字符串和名字依然用了wrapper,但是我需要
        输出add函数中的文挡字符串信息?该怎么办呢?
   要达到的效果是 输出 add add function~~~~~~

 怎么做有思路吗?

问题出在,因为我返回的是wrapper函数,wrapper所指向的函数对象呢,里面的函
	数名已经定义为wrapper了,里面的文档也写死了是
''' wrapper function ++++'''我们把doc文档换掉就好了,但是在哪里换呢?

	wrapper.__name__=fn.__name__
    wrapper.__doc__=fn.__doc__
    
    在wrapper函数中加这两行代码
    封装成函数 
    def copy_properties(dst,src):
        print(dst.__name__,'***',src.__doc__)
        dst.__name__=src.__name__
        dst.__doc__=src.__doc__
        #其他属性省略
        print(dst.__name__, '***', src.__doc__)

二、带参数装饰器

现在的问题是,这个用法比较麻烦能不能改造成装饰器?我们来看代码

def logger(fn):
    @copy_properties(fn)
    def wrapper(*args, **kwargs):
        ''' wrapper function+++++++'''
        print('调用前增强功能')
        start=datetime.datetime.now()
        ret = fn(*args, **kwargs)
        print('调用后增强功能')
        delta=(datetime.datetime.now()-start).total_seconds()
        print("Function {} took {:.2f}s".format(fn.__name__,delta))
        									#delta 输出多少秒
        return ret

    copy_properties(wrapper,fn)
    return wrapper

现在加入了@copy_properties(fn),这个装饰器就是带参数装饰器。
因为这个装饰器参数,需要调用一个函数参数,所以是带参装饰器

我们在来看整体代码

import datetime
import time

#变成装饰器
def copy_properties(src):
    def copy_pro(dst):
        dst.__name__=src.__name__
        dst.__doc__=src.__doc__
    return copy_pro

def logger(fn):
    @copy_properties(fn)  #装饰了fn函数
    def wrapper(*args, **kwargs):
        ''' wrapper function+++++++'''
      
        start=datetime.datetime.now()
        ret = fn(*args, **kwargs)
        delta=(datetime.datetime.now()-start).total_seconds()
        print("Function {} took {:.2f}s".format(fn.__name__,delta))
        #delta 输出多少秒
        return ret
    #copy_properties(wrapper)(fn)#copy_pro(fn)

    return wrapper

@logger  # add = logger(add)
def add(x, y):
    '''add function~~~~~~ '''
    time.sleep(2)
    return x + y

# add = logger(add) 这行代码不需要了注释掉

print(add.__name__,add.__doc__)

上面代码执行会报错的我们来分析下错误原因:

这段代码的执行顺序如下:
	1.首先执行到@logger的时候,它其实是执行了add=logger(add)这个语句,
    因为@+函数标识符是装饰器语法,装饰器他会把它下面最近的函数标识符这里是
	add给抽取上来作为参数。

	因为add=logger(add)本质上是函数调用了,根据优先级原则,先做等式右边的
	logger(add)

	2.进入到logger 之后发现第一行是@copy_properties(fn)发现也是一个装饰
    器我们会发现这个@copy_properties(fn)装饰器后面是有参数的,这个参数是
     fn
      根据第一步我们知道,参数传入进来的其实是add函数。

	  在整个logger函数的执行,会把它下面定义的函数wrapper定义好,在进行
	  @copy_properties这个执行,它实质上也是一次函数调用,等价于
      wrapper=copy_propertties(fn)(wrapper),这是一个有参数的装饰器
	  修饰了wrapper.

    3.第三步我们需要进入到copy_propertties(src)这个函数内部进行执行,传
      进去参数是add函数,这个函数执行完会返回一个函数copy_pro(),我们在把
      参数wrapper给传入进去,这样一来dst和src都知道是谁了,目的是wrapper
	  源是src,add.
      但是copy_pro()函数内部只有两个语句,dst.__name__=src.__name__
      dst.__doc__=src.__doc__,我们又知道,函数内部不写返回值,默认就是
      返回NULL/相当于执行了一个return None。

      在根据装饰器语法@copy_properties(fn),执行这个相当于执行了
      wrapper=copy_propertties(fn)(wrapper),那我们就知道了,现在
      wrapper的值是NULL了,所以后面主函数我们在执行

           print(add.__name__,add.__doc__)会直接报错的

           add=logger(add)-->add=wrapper--->退出add=None
		
	我们怎么修改?让代码变的正确,我们写装饰器的时候,要明确给返回值。
		因为修改的是目的函数,所以我们就要返回dst这个参数函数就看可以了  
		  def copy_properties(src):
			    def copy_pro(dst):
			        dst.__name__=src.__name__
			        dst.__doc__=src.__doc__
			        return dst
                return  copy_pro

         	dst就是wrapper,修改一下返回。返回包装函数本身       

上面这种就是带参装饰器.




标签:__,12,之带,Python,wrapper,add,.__,copy,fn
From: https://blog.51cto.com/u_14743944/6193354

相关文章

  • 求子集--Python解法
    给你一个整数数组nums,数组中的元素互不相同。返回该数组所有可能的子集(幂集)。defsubsets(nums):res=[]self.dfs(nums,0,res,[])returnresdefdfs(nums,index,res,path):res.append(path)foriinrange(index,l......
  • python学习之http客户端和服务端
    Part1前言python非常简洁,非常适合写小功能以及测试接口。本文主要记录用pyhon实现一个简单的http客户端和服务端。Part2http客户端这里采用request库来实现。示例如下importrequestsimportjsonurl='http://127.0.0.1:81/test?key1=123&key2=456'headers={'Authoriza......
  • 在写Python是要注意初始化函数的书写
    问题来啦!在撰写Python程序的时候,我们一定要注意,Python的初始化函数init的书写,它的名称是init,这样的话,实例化的过程中,我们才能够成功获取到我们在里面输入的数值;我起初就忽视了这个问题,将原本的四个横线写成了这样__init,主要是它也没有报出错误,我也获取不到数值,后来又仔细检查......
  • python 批量打印证书(保存未调试)
    importosfromPILimportImage,ImageDraw,ImageFontimportxlrd#要求录入学校信息的证书defzs_school(size,left,height,n,c,m1,d1,m2,d2,t):newfont=ImageFont.truetype(font="Songti.ttc",size=size)draw.text((600,height),n,font=newfont......
  • 全排列--Python实现
    给定一个不含重复数字的数组nums,返回其所有可能的全排列。defpermute(nums):track,self.res=[],[]self.backtrack(nums,track)returnself.res#路径:记录在track中#选择列表:nums中不存在于track的那些元素#结束条件:nums中的......
  • 【CVE-2017-12615】Tomcat 远程代码执行漏洞复现
    0x00环境搭建用vulhub的环境查看配置文件conf/web.xml中readonly的设置0x01漏洞复现访问主页,抓包后修改数据包可通过PUT方式创建一个JSP文件。虽然Tomcat对文件后缀有一定检测(不能直接写jsp),但我们使用一些文件系统的特性(如Linux下可用/)来绕过了限制。改完包的时候......
  • Python 人工智能:6~10
    原文:ArtificialIntelligencewithPython协议:CCBY-NC-SA4.0译者:飞龙本文来自【ApacheCN深度学习译文集】,采用译后编辑(MTPE)流程来尽可能提升效率。不要担心自己的形象,只关心如何实现目标。——《原则》,生活原则2.3.c6集成学习的预测分析在本章中,我们将学习集成学习......
  • Python 人工智能:21~23
    原文:ArtificialIntelligencewithPython协议:CCBY-NC-SA4.0译者:飞龙本文来自【ApacheCN深度学习译文集】,采用译后编辑(MTPE)流程来尽可能提升效率。不要担心自己的形象,只关心如何实现目标。——《原则》,生活原则2.3.c21循环神经网络和其他深度学习模型在本章中,我们将......
  • Python 迁移学习实用指南:6~11
    原文:Hands-OnTransferLearningwithPython协议:CCBY-NC-SA4.0译者:飞龙本文来自【ApacheCN深度学习译文集】,采用译后编辑(MTPE)流程来尽可能提升效率。不要担心自己的形象,只关心如何实现目标。——《原则》,生活原则2.3.c六、图像识别与分类知识投资永远是最大的利益。......
  • Python模块-requests
    1、requests模块requests模块是python中原生的基于网络请求的模块,其主要作用是用来模拟浏览器发起请求。功能强大,用法简洁高效。2、模块介绍及请求过程requests模块模拟浏览器发送请求请求流程:指定url-->发起请求-->获取响应对象中存储的数据-->持久化存储3、爬取百度首页#!......