python中的装饰器

代码环境:python3.6

什么是装饰器

装饰器是 python 的一种语法糖,本质是一个可调用的对象,其参数是一个被装饰的函数。装饰器可能会处理被装饰的函数并返回,或者将其替换成另外一个函数或对象。python

装饰器两种经常使用的用法

  • 原封不动返回被装饰函数

这种用法出如今不少 python web 框架中,例如把 url 地址映射到 http 响应的函数上的注册处。web

下面举个简单的例子:闭包

def register(func):
    # 内部无新函数
    print('running register {}'.format(func))
    return func


@register
def my_func():
    print('running my_func()')


if __name__ == "__main__":
    my_func()
  • 把被装饰函数替换成新函数

大多数装饰器,一般内部会定义一个闭包结构的函数,将其返回替换被装饰函数。app

这种用法最多见,用于不修改原函数的基础上增长额外的功能,好比计算函数的运行时间、输出指定格式日志等。框架

下面用一个装饰器输出函数运行时间:函数

def running_time(func):
    # 内部有新函数
    def print_running_time(*args):
        '''打印函数运行时间'''
        t0 = time.time()
        result = func(*args)
        need_time = time.time() - t0
        print('新列表生成时间(秒):{:.8f}'.format(need_time))
        return result

    return print_running_time


@running_time
def new_list(n):
    '''生成一个新列表'''
    temp_list = []
    for x in range(n):
        temp_list.append(x * (x + 1))
    return temp_list


if __name__ == "__main__":
    print('新列表长度:{}'.format(len(new_list(12345))))
    print('new_list 函数的 __name__ 属性:{}'.format(new_list.__name__))
    print('new_list 函数的 __doc__ 属性:{}'.format(new_list.__doc__))

执行结果:url

running register <function my_func at 0x0000000002889B70>
新列表生成时间(秒):0.00250006
新列表长度:12345
new_list 函数的 __name__ 属性:print_running_time
new_list 函数的 __doc__ 属性:打印函数运行时间

python什么时候执行装饰器

从上述例子中咱们注意到,在调用new_list(12345)打印出结果以前,结果栏先输出了装饰器中的print语句,这说明:日志

装饰器在导入模块@func时当即执行。code

functools.wraps装饰器

在上述结果中,咱们还注意到另外一个特色:new_list函数的__name____doc__属性都被替换成装饰器内部函数的相关属性。因此,咱们须要改进上面的例子,使用functools.wraps装饰器把相关属性从func复制到新函数中。orm

改进例子以下:

from functools import wraps


def running_time(func):
    # 内部有新函数

    @wraps(func)
    # 此处使用 wraps 装饰器
    def print_running_time(*args):
        '''打印函数运行时间'''
        t0 = time.time()
        result = func(*args)
        need_time = time.time() - t0
        print('新列表生成时间(秒):{:.8f}'.format(need_time))
        return result

    return print_running_time


@running_time
def new_list(n):
    '''生成一个新列表'''
    temp_list = []
    for x in range(n):
        temp_list.append(x * (x + 1))
    return temp_list


if __name__ == "__main__":
    print('新列表长度:{}'.format(len(new_list(12345))))
    print('new_list 函数的 __name__ 属性:{}'.format(new_list.__name__))
    print('new_list 函数的 __doc__ 属性:{}'.format(new_list.__doc__))

执行结果:

running register <function my_func at 0x0000000002879B70>
新列表生成时间(秒):0.00250006
新列表长度:12345
new_list 函数的 __name__ 属性:new_list
new_list 函数的 __doc__ 属性:生成一个新列表

观察改进后例子的运行结果,new_list函数的相关属性已恢复正常。

相关文章
相关标签/搜索