一、装饰器作用
装饰器本质上是一个 Python 函数或类,它可以让其他函数或类在不需要做任何代码修改的前提下增加额外功能,装饰器的返回值也是一个函数/类对象。它经常用于有切面需求的场景,比如:插入日志、性能测试、事务处理、缓存、权限校验等场景,装饰器是解决这类问题的绝佳设计。
二、普通装饰器
1、不带参数的装饰器

def use_print(func):
    def wrapper():
        print(f"{func.__name__} is running")
        return func()  # 把 foo 当做参数传递进来时,执行func()就相当于执行foo()

    return wrapper


def foo():
    print('i am foo')


foo = use_print(foo)  # 因为装饰器 use_print(foo) 返回的时函数对象 wrapper,这条语句相当于  foo = wrapper
foo()  # 执行foo()就相当于执行 wrapper()

2、使用了语法糖@
@ 符号就是装饰器的语法糖,它放在函数开始定义的地方,这样就可以省略最后一步再次赋值的操作。

def use_print(func):
    def wrapper():
        print(f"{func.__name__} is running")
        return func()  # 把 foo 当做参数传递进来时,执行func()就相当于执行foo()

    return wrapper


@use_print
def foo():
    print('i am foo')


foo()  # 和最简单的装饰器一样的效果,@use_print等于foo = use_print(foo)

有了 @ ,我们就可以省去foo = use_print(foo)这一句了,直接调用 foo() 即可得到想要的结果
3、带参数的装饰器
如果函数 foo 需要参数如:

def foo(name):
    print("i am %s" % name)

可以在定义 wrapper 函数的时候指定参数:

def use_print(agrs1, args2):
    def wrapper(func):
        def innerfunc(name):
            print(f"{func.__name__} is running,my name is {name}")
            print(agrs1, args2)
            return func(name)  # 把 foo 当做参数传递进来时,执行func()就相当于执行foo()

        return innerfunc

    return wrapper


@use_print('参数1', '参数2')
def foo(name):
    print(f'i am {name}')


foo('jojo')  # foo=use_print('参数1', '参数2')(foo)

如果参数不定时用args、*kwargs

def wrapper(*args, **kwargs):
        # args是一个数组,kwargs一个字典
        logging.warn("%s is running" % func.__name__)
        return func(*args, **kwargs)
    return wrapper

三、类装饰器
装饰器不仅可以是函数,还可以是类,相比函数装饰器,类装饰器具有灵活度大、高内聚、封装性等优点。使用类装饰器主要依靠类的__call__方法,当使用 @ 形式将装饰器附加到函数上时,就会调用此方法
1、不带参数的类装饰器

class Foo:
    def __init__(self, func):
        self._func = func

    def __call__(self):
        print('class decorator runing')
        self._func()
        print('class decorator ending')


@Foo
def bar():
    print('bar')


bar()  # 等效于 bar=Foo(bar)

2、带参数的类装饰器

class Foo:
    def __init__(self, name):
        self.name = name

    def __call__(self, func):
        print('class decorator runing')

        def wrapper(*args, **kwargs):
            func(*args, **kwargs)

        print('class decorator ending')
        return wrapper


@Foo("lalal")
def bar(text):
    print(text)


bar("bar")  # 等效于 bar=Foo("lalal")(bar)

四、装饰器顺序
一个函数还可以同时定义多个装饰器

@a
@b
@c
def f ():
    pass

# 约等于 f = a(b(c(f)))