欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

浅谈装饰器

程序员文章站 2022-05-04 15:18:41
装饰器装饰器本事是一个函数,它可以让其他函数在不需要做任何代码变动的前提下增加额外功能,装饰器的返回值也是一个函数对象。它经常用于有切面需求的场景,比如:插入日志,性能测试,事务处理,缓存、权限校验等场景。装饰器的作用:1.增加额外功能。一般为有共性的额外功能。2.不修改被装饰的函数的源代码和调用方 ......

装饰器
装饰器本事是一个函数,它可以让其他函数在不需要做任何代码变动的前提下增加额外功能,装饰器的返回值也是一个函数对象。它经常用于有切面需求的场景,比如:插入日志,性能测试,事务处理,缓存、权限校验等场景。
装饰器的作用:
1.增加额外功能。一般为有共性的额外功能。
2.不修改被装饰的函数的源代码和调用方式。
3.分离了业务函数和非业务函数。增加了装饰函数的*性、复用性。

例如我有一个加法add函数,需要添加一个日志打印loging功能。
思路一,在加法函数代码上添加。
def add(x,y,z=7):
print('this is a {} func'.format(add.__name__))
return x+y+z
这种思路侵入了add函数,又破坏了日志打印功能的复用性。 
肯定是不可取的。弃用。
思路二,定义一个loging函数。
def add(x,y,z=7):
    return x+y+z

def loging(fn):
    print("this fun is {}".format(fn.__name__))
    res=fn(4,5)
    return res
   
print(loging(add))
做到了业务分离,但是fn函数传参是个问题。
改进版本:
def add(x,y,z=7):
    return x+y+z

def loging(fn,x,y):
    print("this fun is {}".format(fn.__name__))
    res=fn(x,y)
    return res
   
print(loging(add,4,5))
参数*度不高,继续改进,使用无敌参数:
def add(x,y,z=7):
    return x+y+z

def loging(fn,*args,**kwargs):
    print("this fun is {}".format(fn.__name__))
    res=fn(*args,**kwargs)
    return res
   
print(loging(add,4,5))
之前执行业务逻辑时,使用add(4,5),现在不得不使用loging(add,4,5),有没有更好的方式
呢?继续改进:
def add(x,y,z=7):
    return x+y+z

def loging(fn):
    def _loging(*args,**kwargs):
        print("this fun is {}".format(fn.__name__))
        res=fn(*args,**kwargs)
        return res
    return _loging
   
add=loging(add)
print(add(4,5))
貌似很接近了,就是多了一句add=loging(add)。有没有方法去掉呢,当然有,答案就是装饰
器。
装饰器是一个语法糖,在定义函数前+   @装饰函数, 相当于一句赋值:
被装饰函数  = 装饰函数(被装饰函数)。。。概念什么的最烦了。
简单说就是add函数前写@loging,就省去一句add=loging(add)。
太好了,目标达成,啦啦啦。
def loging(fn):
    def wrapper(*args,**kwargs):
        print("this fun is {}".format(fn.__name__))
        res=fn(*args,**kwargs)
        return res
    return wrapper

@loging      #  相当于add=loging(add)
def add(x,y,z=7):
    return x+y+z
   
print(add(4,5))
###########分割#################################
继续科普概念。。。:
装饰器在python使用如此方便都要归因于python的函数能像普通的对象一样能作为参数传递给其
他函数,可以被赋值给其他变量,可以作为返回值,可以被定义在另一个函数内。
装饰器是一个函数,函数作为它的形参,返回值也是一个函数。
functools模块
上面的例子中,虽然add函数的源代码和调用方法没有发生改变,但是add函数实际上已经被 
@loging #  相当于add=loging(add)重新定义为一个增强版的add,这就会产生一个副作用,
即:原函数对象的属性被替换了。add的属性被替代为了wrapper的属性!!好在我们有
functools.wraps,wraps本身也是一个装饰器,它能把原参数的元信息拷贝到装饰器函数中,这
使得装饰器函数也有和原函数一样的元信息了。
用法:
@functools.wraps(wrapped, assigned=WRAPPER_ASSIGNMENTS,
updated=WRAPPER_UPDATES)
类似copy_properties功能
wrapped 被包装函数
元组WRAPPER_ASSIGNMENTS中是要被覆盖的属性
'__module__', '__name__', '__qualname__', '__doc__', '__annotations__'
模块名、名称、限定名、文档、参数注解
元组WRAPPER_UPDATES中是要被更新的属性,__dict__属性字典
增加一个__wrapped__属性,保留着wrapped函数
import functools
def loging(fn):
    @functools.wraps(fn)
    def wrapper(*args,**kwargs):
        print("this fun is {}".format(fn.__name__))
        res=fn(*args,**kwargs)
        return res
    return wrapper

@loging      #  相当于add=loging(add)
def add(x,y,z=7):
    return x+y+z
   
print(add(4,5))
print(add.__name__)

带参装饰器:
装饰器还有更大的灵活性,例如带参数的装饰器,在上面的装饰器调用中,如loging,
该装饰器唯一的参数就是执行业务的函数。装饰器的语法允许我们在调用时,提供其他参数,
如:@decorator(a),这样,就为装饰器的编写和使用提供了更大的灵活性。
def loging(level):
    def decorator(fn):
        def wrapper(*args,**kwargs):
            print("this fun is {},level is {}".format(fn.__name__,level))
            res=fn(*args,**kwargs)
            return res
        return wrapper
    return decorator

@loging(level='warn')      #  相当于add=loging(level)(add)  decorator(add)
def add(x,y,z=7):
    return x+y+z
   
print(add(4,5))