Python装饰器

Python装饰器

简单装饰器

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

    return wrapper


def foo():
    print("I'm foo")


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

print_func 就是一个装饰器,他是一个普通函数。 他把真正的业务函数func包裹在里面,看起来foo就像被print_func函数装饰了一样。print_func返回的也是一个函数,这个函数的名字是wrapper
。在这个例子中,函数的进入和退出,被称为一个横切面,这就是面向切面的编程。

@ 语法糖

"@"符号是Python装饰器的语法糖。什么是语法糖自行Google,简单可以理解为一个简单快捷的语法。将他放在要执行的函数前面,就可以省略上个例子中的赋值的过程,代码如下

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

    return wrapper


@print_func
def foo():
    print("I'm foo")


foo()

装饰器如此方便的一个原因就是因为Python有个特殊的性能,它可以将方法作为参数传递给其他的方法,可以被赋值给其他变量,可以作为返回值,可以被定义在另一个方法内

*args, **kwargs

如果foo方法需要带参数,该怎么处理呢,如

def foo(name):
    print(f'I am {name}')

此时我的装饰器wrapper应该怎么写呢?当业务函数需要带参数时,wrapper函数也可以需要带参数进行处理,代码如下

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

    return wrapper

此时就可以满足带参数的方法了。但是不满足有多个参数。所以为了同时满足带参数或者不带参数的情况,装饰器的参数应该是不定长度,使用*args.装饰器应该是如下写法:

def print_func(func):
    # 当函数的参数个数不确定且不需要指定参数名称时
    # *args的格式是常规的参数 val1[,val2,val3....]
    def wrapper(*args):
        print(f'{func.__name__} is running ')
        return func(*args)

    return wrapper

当参数个数不确定且带有参数名字时,我们使用**kwargs放入装饰器wrapper中,如下

def print_func(func):
    # 当函数的参数是有名称且不确定个数的时候,可以使用**kwargs
    # **kwargs的参数格式是 key1=value1,[key2=value2,key3=value3,....]
    # 函数对**kwargs是以键值对类似字典的方式进行解析。
    def wrapper(**kwargs):
        print(f'{func.__name__} is running ')
        return func(**kwargs)

    return wrapper

装饰器带参数

由于装饰器是一个普通函数,所以他接收的参数除了业务函数之外,还可以接受其他的参数,进行更为复杂的业务处理。比如,我们在打印这个函数名字的时候,可以根据我们传入的参数进行判断,如果传入的是yes,就进行打印,否则输出错误提示,具体代码如下:

def print_func(isprint):
    def decorator(func):
        def wrapper(*args, **kwargs):
            if isprint == 'yes':
                print(f'{func.__name__} is running ')
            else:
                print('input is wrong...')
            return func(*args, **kwargs)

        return wrapper

    return decorator


@print_func(isprint='no')
def foo(name):
    print(f"I'm {name}")


foo('Tom')
posted @ 2021-06-15 21:42  Hiraly  阅读(80)  评论(0)    收藏  举报