Python入门笔记之装饰器decorator

先来看一段代码:

def f1(x):
    return x*2
def f2(x):
    return x*3
def f3(x):
    return x*4
print f1(1),f2(1),f3(1)

如果我想在每个方法的执行结束前,都输出一下当前方法名,那么该怎么做呢?最直接的方法就是直接修改函数,在返回值前加个输出,代码会变成下面的这个样子:

def f1(x):
    print 'this is f1'
    return x*2
def f2(x):
    print 'this is f2'
    return x*3
def f3(x):
    print 'this is f3'
    return x*4
print f1(1),f2(1),f3(1)

这当然是最直接的方法,那么有没有更简单的方法呢?答案是有的。再来看看一下这段代码:

def new_fn(f):
    def fn(x):
        print 'this is '+f.__name__
        return f(x)
    return fn
f1 = new_fn(f1)
print f1(1)

要是我们通过一个高阶函来返回这个函数,然后再将它赋值给f1,这样我们就改写了f1,并且彻底隐藏原f1了。这种其实就是装饰器的用法。

而在Python中,它有一种@语法,其实就是为了简化装饰器的调用的。那么装饰器有什么作用呢?装饰器可以极大地简化代码,避免每个函数编写重复性代码。当我们再定义一个函数时,如果想用装饰器,我们就可以这样写:

@new_fn
def f4(x):
    return x*5
print f4(1)

这样的代码是不是比刚刚的代码简便了?

此外,decorator也分为带参数和无参数两种情况。无参数时:

import time

def performance(f):
    def fn(*args,**kw):
        t1 = time.time()
        r = f(*args,**kw)
        t2 = time.time()
        print 'call %s() in %fs'%(f.__name__,(t2 - t1))
        return r
    return fn

@performance
def factorial(n):
    return reduce(lambda x,y: x*y, range(1, n+1))

print factorial(10)

这时的@performance是没有带参数的,但是有时候为了拓展性考虑,我们可能也需要它能传入一些参数,这时函数就可以变成下面的样子:

import time

def performance(unit):
    def perf_decorator(f):
        def wrapper(*args,**kw):
            t1 = time.time()
            r = f(*args,**kw)
            t2 = time.time()
            t = (t2 - t1) * 1000 if unit == 'ms' else (t2 - t1)
            print 'call %s() in %f %s' %(f.__name__,t,unit)
            return r
        return wrapper
    return perf_decorator

@performance('ms')
def factorial(n):
    return reduce(lambda x,y: x*y, range(1, n+1))

print factorial(10)

    给@performance加入一个参数,用来判断是返回秒(s)还是返回微秒(ms),这样就可以提高代码的拓展性了。

    最后,其实用装饰器改造的函数时会有不同的,我们再执行一下下面的这段代码:

def f1(x):
    pass
print f1.__name__
#输出值为:f1
def log(f):
    def wrapper(*args, **kw):
        print 'call...'
        return f(*args, **kw)
    return wrapper
@log
def f2(x):
    pass
print f2.__name__
#输出值为:wrapper

由上面的代码可以看出,decorator返回的新函数名字已经变了,变成了log内部定义的wrapper了,这时如果其他地方有依赖着这个函数名的代码的话,就会失效。这时,如果我们想不让调用者看到函数被改造过了,那么久需要将其原有的属性复制到这个新的函数里面去。Python内置的functools就可以帮我们自动化地完成这个“复制”的任务了:

import functools
def log(f):
    @functools.wraps(f)
    def wrapper(*args, **kw):
        print 'call...'
        return f(*args, **kw)
    return wrapper
@log
def f2(x):
    pass
print f2.__name__
#输出值为:f2

以上就是Python装饰器的一些小笔记

P.S.以上代码大部分摘自慕课网。

转载于:https://my.oschina.net/lonelycode/blog/716590

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值