python装饰器学习总结

什么是装饰器呢?

我的理解python中的装饰器就是对一个函数调用前后增加一些新的代码。

装饰器是一个函数,但是它里面重新定义了一个新的函数,这个新的函数里面包含对一个指定函数的调用。最终装饰器返回这个函数的引用。当然装饰器不仅可以作用于一个函数,也可以作用于一个类。

举一个简单的装饰器的例子

def log_func_name(func):
    '''打印函数名'''
    def wrapper(*args, **kwargs):
        print('函数{}正在运行'.format(func.__name__))
        func(*args, **kwargs)
    return wrapper

@log_func_name
def say_hello(cnt):
    '''打印hello'''
    for _ in range(cnt):
        print('hello')

if __name__ == '__main__':
    say_hello(3)

其实@log_func_name的语法糖,相当于在函数声明后加上say_hello = log_func_name(say_hello)语句。
所以上面的代码可以等价为

def log_func_name(func):
    '''打印函数名'''
    def wrapper(*args, **kwargs):
        print('函数{}正在运行'.format(func.__name__))
        func(*args, **kwargs)
    return wrapper

def say_hello(cnt):
    '''打印hello'''
    for _ in range(cnt):
        print('hello')
say_hello = log_func_name(say_hello)

if __name__ == '__main__':
    say_hello(3)

如果上述代码在ipython等交互环境上运行,我们还可以运行

say_hello.__name__
# 输出 'wrapper'

由此可以看书say_hello函数并不是我们直接声明的say_hello函数,实际上say_hello指向log_lineno函数返回的wrapper函数。即上文所说,@log_func_name相当于执行了say_hello = log_lineno(say_hello)这句命令。

问题来了,如果say_hello函数带有参数那该怎么办?因为say_hello其实是指向wrapper的,所以say_hello能接受的参数,wrapper也必须能接受。那么对应say_hello函数的装饰器函数代码要如下所示

def log_func_name(func):
    '''打印函数名'''
    def wrapper(cnt):
        print('函数{}正在运行'.format(func.__name__))
        func(cnt)
    return wrapper

@log_func_name
def say_hello(cnt):
    '''打印hello'''
    for _ in range(cnt):
        print('hello')

上述方法是最简单粗暴的方法,即保持wrapper函数和say_hello函数的参数一致。
这样问题就来了,一个装饰器不可能只用于一个函数啊,不然直接在那个函数上修改就完事了。
即装饰器的使用范围要有广泛性,比如log_func_name这个装饰器是用于打印函数名,这个装饰器对所有的函数都生效,不管函数多复杂。

这个时候就要用到*args**kwargs,要想知道这两个参数有什么,收到运行下面代码就知道啦

def print_args(*args, **kwargs):
    print(args)
    print(kwargs)

print_args(1,2,3)
# (1, 2, 3)
# {}
print_args(1,2,3,a=1,b=2)
# (1, 2, 3)
# {'a': 1, 'b': 2, 'c': 3}

可以看书,args就是一个参数的数组,kwargs是一个以参数名作为key的字典(关键字参数的字典)
*args前面的*用于将args数组解包,即传入的不是args数组,而是将args数组里面的元素都当成参数传用。同理**用于将字典解包。
这样我们就解决了装饰器的适用性问题了,开心!!

你可能感兴趣的:(python装饰器学习总结)