个人博客

python中的装饰器

python中的装饰器本质上是一个函数,它可以让其他函数在不做任何改变的情况下增加额外的功能,装饰器返回的是一个函数对象。它经常用于插入日志、权限检验、登陆验证等场景。

在理解装饰器之前,我们想理解下闭包的概念。

闭包:在计算机科学中,闭包,又称为词法闭包,或者函数闭包,是引用了自由变量的函数。这个被引用的自由变量将和这个函数一同存在,即使已经离开了创造它的环境也不例外。

简单的看个例子:

def func():
    data = 'the data value'
    def getdata():
        print(data)
    return getdata

# 这里获取的就是一个闭包
data_value = func()
# 这里打印‘the data value’
data_value()

这里的data就是一个局部变量,在func函数执行之后,该变量就不存在了。但是里面的嵌套函数使用了这个变量,将这个变量封闭在嵌套的函数中,这样就形成了一个闭包。

装饰器的使用,也和这个闭包类似,可以看看平时封装的一个简单的插入日志的装饰器:

import functools

def log(func):
    @functools.wraps(func):
    def wrapper(*args, **kwargs)
        print('call  %s():' % func.__name__)
        print('args = {}'.format(*args))
        return func(*args, **kwargs)
    return wrapper

@log
def test(msg):
    print('test function')
    print('msg: ', msg)

test('hello')

使用装饰器时,使用了@语法,其实本质上来讲,它也就是一个函数,参数时函数对象,返回也是函数对象,和下面这种方式没有任何区别:

import functools

def log(func):
    @functools.wraps(func):
    def wrapper(*args, **kwargs)
        print('call  %s():' % func.__name__)
        print('args = {}'.format(*args))
        return func(*args, **kwargs)
    return wrapper

def test(msg):
    print('test function')
    print('msg: ', msg)

wrapper = log(test)
wrapper('hello')

当然这个装饰器里面要关注的是@functools.wraps(func)语句,这个python自带的一个装饰器,主要作用就是将原函数的元信息拷贝到装饰器里的func对象,这样可以获取到被调用的函数的name、参数列表等等。

装饰器也是可以带参数,代码如下:

import functools

def record_by_name(name):
    def decorator(func):
        @functools.wraps(func)
        def wrapper(*args, **kwargs)
            print('call  %s():' % func.__name__)
            print('args = {}'.format(*args))
            print('record by %s.' % name)
            return func(*args, **kwargs)
        return wrapper
    return decorator

@record_by_name('reda')
def test(msg):
    print('test function')
    print('msg: ', msg)

test('hello')

这个带参数的装饰器,看上去挺复杂的,但是在理解了普通的装饰器的使用之后,其实很好理解。只要把最外层的record_by_name函数拿走,此时里面的decorator就和之前的log装饰器一模一样了。而record_by_name('reda')返回的就是decorator函数对象,也就是之前的log函数对象,所以@record_by_name('reda')等同于@log,这样理解起来就是和之前的装饰器没有啥区别了,就是多传递了一个参数值。

装饰器在python中使用的还是比较多的,在以后使用过程中会有更大的体会。

相关标签
回到顶部