python_装饰器

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

1. 普通装饰器

  装饰器就是一个函数,它的返回值是一个也是闭包函数对象,主要是拓展函数功能,在不改变原函数代码结构的情况下附加操作达到业务需求。比如做一些身份认证、误操判断、访问记录一些预处理操作,操作记录、事务处理这样的结束附加操作,又或者是性能测试这种上下都附加代码的操作。

  直接就上一个的基本的装饰器代码  

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有装饰标记,直接去寻找命名为deco的函数,记录了func接收的参数们的内存地址

    2. 将func函数对象作为参数传入来调用deco,执行时将参数func绑定到inner函数对象上,返回inner函数对象(目测func的参数此时应该不会绑定到inner上)

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

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

  没有错,相当于合成并调用了一个调用了旧函数的新函数。

  但是好像我们去掉装饰器这个标记,还原为两个正常的函数,也能实现上面的逻辑吧:

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

def 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 inner1

def 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有装饰标记,直接去寻找包装最近命名为deco2的函数,记录了func接收的参数们的内存地址

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

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

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

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

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

3.类装饰器

      这个好像就比较特别一点了,但仔细想想跟普通的装饰器也是一样的原理

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}

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

    1. 判断func有装饰标记,直接去寻找命名为Deco的对象,记录了func接收的参数们的内存地址

    2. 将func函数对象作为参数传入来调用Deco的__init__方法,执行时将参数func绑定到类实例属性上,返回类实例对象

    3. 调用类实例对象(类的__call__方法可以让类实例可以像函数一样调用),传入之前记录的参数们,执行预操作(验证身份)

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

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,但是肯定也是做了处理的。

以上就是一些装饰器的介绍,如有错误,还请指出[email protected]

猜你喜欢

转载自www.cnblogs.com/MilletChili/p/9175343.html
今日推荐