装饰器(Decorator)

1. 函数也是对象

在python中,函数也是对象,它有自己的方法,它可以传递下去。

函数传递

>>> def brown():
        print 'brown'

>>> new_brown = brown  # 函数对象传递
>>> new_brown()
brown

2. 什么是装饰器

装饰器实际上是函数,它以函数对象为参数,可以在它所装饰的函数的前或后添加一些其它的操作。你可以在执行(被装饰的)函数之前做一些预准备工作,也可以在执行(被装饰的)函数之后做一些清理工作,比如事后分析

通常,当你wrap一个函数时,你应该在wrapper里调用它。你可以在wrapper里面随时调用它。

>>> from functools import wraps
>>> def deco(func):  # 定义装饰器
        @wraps(func)
        def wrapper(*args, **kw_args):
            print 'Do something prepared!'
            func(*args, **kw_args)
            print 'Do something cleanup!'
        return wrapper

>>> @deco  # 使用装饰器
    def hello():
        print 'Hello Brown!'
>>> hello()
Do something prepared!
Hello Brown!
Do something cleanup!

注意

  1. 使用装饰器时使用了下述语法:

    @deco
    def foo():
        # To do something

    其实等价于下述语法:

    def foo():
        # To do something
    foo = deco(foo)
  2. 定义装饰器时,在wrapper()函数上使用了functools.wraps装饰器,目的是保留元信息(名字、文档字符串、注解和参数签名等)。你可以不使用它,函数照样能运行,但是当你执行

    hello.__name__
    

    你会发现hello的函数名不再是hello了,这意味着元信息丢了。

3. 装饰器的作用

装饰器可以用来:

  • 引入日志
  • 增加计时逻辑来检测性能
  • 给函数加入事务的能力

4. 带参数的装饰器

注意,上面所讲的装饰器是没有参数的,e.g.@deco;如果我们需要给装饰器传递参数,我们需要:定义一个返回(以被装饰函数作为参数的)装饰器的函数。有点拗口,简单地说,就是在我们之前的装饰器函数外再包裹一层函数,这个函数返回我们定义的无参数装饰器。

>>> from functools import wraps
>>> def deco_param(param):
        def deco(func):
            @wraps(func)
            def wrapper(*args, **kw_args):
                print 'Do something prepared!'
                print 'Param is %s!' % param
                func(*args, **kw_args)
                print 'Do something cleanup!'
            return wrapper
        return deco

>>> @deco_param('BrownWong')
def hello():
    print 'Hello Brown!'


>>> hello()
Do something prepared!
Param is BrownWong!
Hello Brown!
Do something cleanup!

注意

@deco_param(param)
def hello():
    # To do something

等价于

def hello():
    # To do something
hello = deco_param(param)(hello)

5. 堆积装饰器

装饰器可以堆积起来,如下

@deco1
@deco2
def func():
    # To do something   

它等价于:

func = deco1(deco2(func))

6. 使用装饰器为函数打日志

我们看一个例子,例子会写一个用来打日志的装饰器,装饰器可以打印被装饰函数的位置、函数名、输入参数、函数消耗时间、函数返回值。

函数版本

from functools import wraps

# 定义装饰器函数
def log_deco(logger):
    def wrapper1(func):
        @wraps(func)
        def wrapper2(*args, **kw_args):
            init_time = time.time()
            result = func(*args, **kw_args)
            spend_time = time.time() - init_time
            logger.info('{}: {}(), params: {}, {}, result: {}, spend time: {}'.format(func.__module__, func.func_name, args, kw_args, result, spend_time))
            return result
        return wrapper2
    return wrapper1

# 使用装饰器函数
@log_deco(logger)
def my_func(a, b, c=1):
    time.sleep(1)
    return a + b + c

if __name__ == '__main__':
    my_func(1, 2, c=3)

类版本

from functools import wraps

# 定义装饰器类
class LogDeco(object):
    def __init__(self, logger):
        self.logger = logger

    def __call__(self, func):
        @wraps(func)
        def wrapper(*args, **kw_args):
            init_time = time.time()
            result = func(*args, **kw_args)
            spend_time = time.time() - init_time
            self.logger.info('{}: {}(), params: {}, {}, result: {}, spend time: {}s'.format(\
                func.__module__, func.func_name, args, kw_args, result, spend_time))
            return result
        return wrapper

# 使用装饰器
@LogDeco(logger)
def my_func(a, b, c=1):
    time.sleep(1)
    return a + b + c

if __name__ == '__main__':
    my_func(1, 2, c=3)


Ref

http://www.liaoxuefeng.com/wiki/001374738125095c955c1e6d8bb493182103fac9270762a000/001386819879946007bbf6ad052463ab18034f0254bf355000
《Python核心编程》(第二版)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值