装饰器decorator

Python的装饰器decorator本质上是一个高阶函数,它接收一个函数作为参数,然后返回一个新的函数,可以让该函数在不改动源代码的情况下增加其他新功能。

python通过一个语法糖@符号来使用decorator,这样可以避免编写f = decorate(f)这样形式的代码。所谓的语法糖便是你不使用也可以完成任务,但是使用它可以让你的代码更简洁。

对于装饰器,需要记住的就是

@decoratedef f():
    pass

其中,

@decorate   等价于  f = decorate(f)


无参数decorator

示例:

import timedef performance(f):
    def fn(*args, **kw):
        t1 = time.time()				#unix时间戳
        r = f(*args, **kw)
        t2 = time.time()
        print 'call %s() in %f s' % (f.__name__, t2 - t1)
        return r    return fn

@performancedef factorial(n):
    return reduce(lambda x,y: x*y, range(1, n+1))print factorial(10)

执行结果:

call factorial() in 0.002750 s
3628800

@performance可以打印出factorial()函数调用的时间。

利用python的*args**kw,可以让@performance自适应任何参数定义的函数,保证任意个数的参数总是能正常调用。

@performance 完全等价于 factorial = performance(factorial)

对于performance()函数,形成一个闭包。程序首先调用performance(factorial),得到的返回结果赋值给了factorial,这样factorial就变成了指向函数fn()的指针,经过装饰后的factorial(),其实是调用fn()


带参数decorator

示例:

import timedef 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)

执行结果:

call factorial() in 2.448082 ms
3628800

@performance('ms') 完全等价于 factorial = performance('ms')(factorial)

factorial = performance('ms')(factorial)展开一下就是:

perf_decorator = performance('ms')

factorial = perf_decorator(factorial)

perf_decorator = performance('ms')@perf_decoratordef factorial():
    pass

所以,带参数的performance()函数首先返回一个decorator函数,再让这个decorator函数接收factorial并返回新函数。

对于performance()函数,形成一个闭包;对于perf_decorator()函数,也形成一个闭包。程序首先调用performance('ms'),返回perf_decorator,再将perf_decorator(factorial)得到的返回结果赋值给了factorial,这样factorial就变成了指向函数wrapper()的指针,经过装饰后的factorial(),其实是调用wrapper()


此外,python还有内置函数property(),用作装饰器时可以很方便的创建只读属性。

python内置函数:https://docs.python.org/zh-cn/3/library/functions.html