Python中的修饰器

Decorator

有种设计模式叫修饰器模式, 它可以在不修改目标函数代码的前提下, 在目标函数执行前后增加一些额外功能, 例如函数计时.

模块decorator_demo.py:

import time

def time_it(fn):
    print ('time_it is executed')
    def new_fn(*args):
        start = time.time()
        result = fn(*args)
        end = time.time()
        duration = end - start
        print('%s seconds are consumed in executing function:%s%r'\
              %(duration, fn.__name__, args))
        return result

    return new_fn

@time_it    
def acc1(start, end):
    s = 0
    for i in xrange(start, end):
        s += i
    return s


def acc2(start, end):
    s = 0
    for i in xrange(start, end):
        s += i
    return s

print acc1
print acc2

if __name__ == '__main__':
    acc1(10, 1000000)

可以看出, 修饰器是一个函数, 它需要返回一个新的function(示例中的new_fn). 函数通常在被修饰函数(示例中的acc1)执行前后进行一些额外的操作, 例如计时. 这个新的函数一般不会修改被修饰函数的返回结果.

执行脚本:

python decorator_demo.py

可以看到函数执行时间(在最后一行):

time_it is executed
<function new_fn at 0x7fde83bfd9b0>
<function acc2 at 0x7fde83bfda28>
0.0198199748993 seconds are consumed in executing function:acc1(10, 1000000)

执行时机

在模块加载时就会执行修饰器函数, 生成一个个被修饰的新的函数. 这两点可简单验证.
执行:

python
>import decorator_demo

python -c 'import decorator_demo'

可以看到输出:

time_it is executed
<function new_fn at 0x7f5dbb17a9b0>
<function acc2 at 0x7f5dbb17aa28>

第一行的输出可以证明修饰器在import模块时执行.
第二和第三行的输出可以看出被修饰后的函数已经不是原来的函数了.实际上, acc1=time_it(acc1). 这样带来的坏处时屏蔽了原函数的元信息, 例如__name__, __doc__. 如果要保留原函数的信息, 可使用标准库中的修饰器functools.wrap:

def time_it(fn):
    print ('time_it is executed')
    @functools.wraps(fn) #########################
    def new_fn(*args, **kws):
        start = time.time()
        result = fn(*args, **kws)
        end = time.time()
        duration = end - start
        print('%s seconds are consumed in executing function:%s%r'\
              %(duration, fn.__name__, args))
        return result

    return new_fn

再执行:

python -c 'import decorator_demo'

输出为:

time_it is executed
<function acc1 at 0x7ff9e3f07b90>
<function acc2 at 0x7ff9e3f07c08>

可以看出, 被修饰的acc1acc2一样保留了它原来的名字.

带参数的修饰器

不说别的, 上面代码中的functools.wraps就是一个带参数的修饰器.
下面, 我们也来实现一个带参数修饰器: 修改time_it, 通过传入参数keep_meta来选择是否保留原函数的元信息.

import time
import functools

def time_it(keep_meta = False):
    def real_dec(fn):
        if keep_meta:
            @functools.wraps(fn)
            def new_fn(*args):
                start = time.time()
                result = fn(*args)
                end = time.time()
                duration = end - start
                print('%s seconds are consumed in executing function:%s%r'\
                      %(duration, fn.__name__, args))
                return result
        else:
            def new_fn(*args):
                start = time.time()
                result = fn(*args)
                end = time.time()
                duration = end - start
                print('%s seconds are consumed in executing function:%s%r'\
                      %(duration, fn.__name__, args))
                return result

        return new_fn
    return real_dec

@time_it(keep_meta = False)
def acc1(start, end):
    s = 0
    for i in xrange(start, end):
        s += i
    return s


@time_it(keep_meta = True)
def acc2(start, end):
    s = 0
    for i in xrange(start, end):
        s += i
    return s

print acc1
print acc2

if __name__ == '__main__':
    acc1(10, 1000000)```

其实就是在外面又套了一层函数. 然后在修饰函数时, 一定要传入参数:

@time_it(keep_meta = False)
def target_fn

其效果相当于:

target_fn = time_it(keep_meta = False)(target_fn)

执行代码得到输出:

<function new_fn at 0x7fe37cc63c08>
<function acc2 at 0x7fe37cc63cf8>
0.0195889472961 seconds are consumed in executing function:acc1(10, 1000000)

可以看到, 因为keep_meta参数的原因, acc2的元信息被保留而acc1没有.

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值