首页 技术 正文
技术 2022年11月8日
0 收藏 489 点赞 1,841 浏览 3225 个字

越来越觉得写一点技术博客是有多么重要了,明日复明日,现在就开始写吧!

1. 普通装饰器

  装饰器的写法是一种语法糖,装饰器也还是一个函数而已,它接收一个函数对象作为参数,并返回一个新函数,主要是拓展原函数功能,在不改变原函数代码结构的情况下附加操作达到业务需求。比如做身份认证、访问记录等预处理操作,或者是日志记录、通道关闭等收尾操作,又或者是性能测试这种前后都附加代码的操作。

  直接就上一个的基本的装饰器代码  (用print语句代替功能实现)

def deco(fun):
def inner(*args,**kwargs):
print('Successful authentication') # 预处理操作,验证身份
fun(*args,**kwargs) # 原操作
return inner@deco
def func(*args,**kwargs):
print(args,kwargs) func(1,2,a=3)
# Perform pretreatment
# (1, 2) {'a': 3}

  当调用func时,伪流程就是:

    1. 先记录了func接收的参数们的内存地址

    2. 将func函数对象作为参数传入来调用deco,执行时将参数func绑定到inner函数对象上,返回inner函数对象

    3. 调用inner函数,传入之前记录的参数们,执行预操作(验证身份)

    4. 调用func函数,传入inner接收的参数们,执行原操作

  相当于合成并调用了一个新函数,新函数里调用了旧函数。但是好像我们去掉装饰器这个标记语法糖,还原为两个正常的函数,也能实现上面的逻辑吧:

def deco(fun):
def inner(*args,**kwargs):
print('Successful authentication') # 预处理操作,验证身份
fun(*args,**kwargs)  # 原操作
return innerdef func(*args,**kwargs):
print(args,kwargs)  

new_fun = deco(func)
  new_fun(1,2,a=3)
  # Perform pretreatment
  # (1, 2) {‘a’: 3}

  但真实的流程是装饰器在文件载入的时候,就已经处理了被装饰器标记的函数,也就是说当文件载入时,所有被标记的函数都已经变成了装饰器返回的函数了(各自信息唯一),当调用的时候也就直接调用的此函数了。

2.装饰链

  也就是多层装饰器嵌套了一下

def deco1(fun):
def inner1(*args,**kwargs):
print('Successful authentication') # 预处理操作,验证身份
fun(*args,**kwargs)
return inner1def deco2(fun):
def inner2(*args,**kwargs):
print('Successful access record') # 预处理操作,访问记录
fun(*args,**kwargs)  # 原操作
return inner2@deco1
@deco2
def func(*args,**kwargs):
print(args,kwargs)func(1,2,a=3)
# Perform pretreatment
# (1, 2) {'a': 3}

  需要注意的是包装顺序,距离最远的是最外层的包装,就像穿的衣服一样,要先穿贴身的内衣,然后才是外套什么的

  此时调用func时,基本的流程就是:

    1. 记录了func接收的参数们的内存地址

    2. 将func函数对象作为参数传入来调用deco2,执行时将参数func绑定到inner2函数对象上,返回inner2函数对象

    3. 将inner2函数对象作为参数传入来调用deco1,执行时将参数inner2绑定到inner1函数对象上,返回inner1函数对象

    4. 调用inner1函数,传入之前记录的参数们,执行预操作1(验证身份)

    5. 调用inner2函数,传入inner1接收的参数们,执行预操作2(访问记录)

    6. 调用func函数,传入inner2接收的参数们,执行原操作

3.类装饰器

类的__call__方法可以让类实例可以像函数一样调用,所以跟普通的区别不大

class Deco(object):
def __init__(self, fun):
self.fun = fun
def __call__(self,*args,**kwargs):
print("Successful authentication") # 预处理 身份验证
self.fun(*args,**kwargs)@Deco
def func(*args,**kwargs):
print("func")func(1,2,a=3)
# Perform pretreatment
# (1, 2) {'a': 3}

4. 装饰器参数

  利用闭包绑定了参数

def deco_param(*dargs,**dkwargs)
def deco(fun):
def inner(*args,**kwargs):
print('deco_param:',dargs,dkwargs) # 打印装饰器参数
print('Successful authentication') # 预处理操作,验证身份
fun(*args,**kwargs)
return inner
return deco@deco(4,b=5)
def func(*args,**kwargs):
print(args,kwargs)  # 原操作func(1,2,a=3)
# Perform pretreatment
# (1, 2) {'a': 3}

  

5.wraps恢复原函数信息

 按照之前的第一个装饰器例子,打印原函数名,发现被装饰的函数信息已经被丢失,变成了装饰器返回的函数的信息

def deco(fun):
def inner(*args,**kwargs):
print('Successful authentication') # 预处理操作,验证身份
fun(*args,**kwargs)
return inner@deco
def func(*args,**kwargs):
print(args,kwargs)  # 原操作
print(func.__name__)  
# inner

  要想恢复函数之前的信息,那么就需要用上functools包,在闭包函数上加上一个带参的装饰器,逻辑上只是加长了装饰链,而这个装饰器的拓展功能正是找回原函数(多层装饰器中可以是上一个装饰器的返回函数)的信息

from functools import wraps
def deco(fun):
  @wraps(fun)
def inner(*args,**kwargs):
print('Successful authentication') # 预处理操作,验证身份
fun(*args,**kwargs)
return inner@deco
def func(*args,**kwargs):
print(args,kwargs)  # 原操作print(fun.__name__)
# func

  ps:一般情况不用找回函数信息,但在使用flask时,flask中不允许接口函数重名导致覆盖或被覆盖,如果有多个接口使用同一个装饰器的话,正常情况下接口函数信息就都为装饰器中的闭包函数信息,这样flask会报AssertionError: View function mapping is overwriting an existing endpoint function xx.inner的错误,此时就可以用上functools的wraps装饰器来找回原函数信息了。而flask必带的接口装饰器route查看源码似乎没有用到wraps,但是肯定也是做了处理的。

以上就是一些装饰器的介绍,如有错误,还请指出—–1738268742@qq.com

相关推荐
python开发_常用的python模块及安装方法
adodb:我们领导推荐的数据库连接组件bsddb3:BerkeleyDB的连接组件Cheetah-1.0:我比较喜欢这个版本的cheeta…
日期:2022-11-24 点赞:878 阅读:9,110
Educational Codeforces Round 11 C. Hard Process 二分
C. Hard Process题目连接:http://www.codeforces.com/contest/660/problem/CDes…
日期:2022-11-24 点赞:807 阅读:5,584
下载Ubuntn 17.04 内核源代码
zengkefu@server1:/usr/src$ uname -aLinux server1 4.10.0-19-generic #21…
日期:2022-11-24 点赞:569 阅读:6,431
可用Active Desktop Calendar V7.86 注册码序列号
可用Active Desktop Calendar V7.86 注册码序列号Name: www.greendown.cn Code: &nb…
日期:2022-11-24 点赞:733 阅读:6,202
Android调用系统相机、自定义相机、处理大图片
Android调用系统相机和自定义相机实例本博文主要是介绍了android上使用相机进行拍照并显示的两种方式,并且由于涉及到要把拍到的照片显…
日期:2022-11-24 点赞:512 阅读:7,837
Struts的使用
一、Struts2的获取  Struts的官方网站为:http://struts.apache.org/  下载完Struts2的jar包,…
日期:2022-11-24 点赞:671 阅读:4,920