装饰器是一个很著名的设计模式,经常被用于有切面需求的场景,较为经典的有插入日志,性能测试,事务处理等。装饰器是为了解决这类问题的绝佳设计,抽离出大量函数中与函数本身无关的雷同代码并继续重用。装饰器的作用就是为了已经存在的对象添加额外的功能。

装饰器是一个函数,用来包装函数的函数。

第一步,一个简单的函数

#!/usr/bin/python
def myfunc():
    print("myfunc() called")
myfunc()
执行结果:

myfunc() called
第二步,我想测试这个函数的时间性能。

#!/usr/bin/python
import time
def myfunc():
    start = time.clock()
    print("myfunc() called")
    end = time.clock()
    print ('time used is ',end-start)
myfunc()
执行结果:

myfunc() called
time used is  0.009169672696094571

可是,如果我还有一个函数叫myfunc2,我也想测试这个函数的时间开销。那么我是否需要复制一份代码呢,只是该一个函数名,这样太累赘了。

将测试时间开销的函数独立出来,这样就可以将业务和数据分离,代码可以很好的重用了。

第三步,分离业务逻辑和测试数据

#!/usr/bin/python
import time
def myfunc():
    print("myfunc() called")

def timeit(func):
    start = time.clock()
    func()
    end = time.clock()
    print ('time used is ',end-start)
timeit(myfunc)

执行结果:

myfunc() called
time used is  0.01921845603380964

当我想在调用myfunc()的时候,就已经有测试时间性能的功能,而不需要额外的修改代码,即调用myfunc的效果相当于timeit(myfunc)

第四步,最少的改动

#!/usr/bin/python
import time
def myfunc():
    print("myfunc() called")

def timeit(func):
    def wrapper():
        start = time.clock()
        func()
        end = time.clock()
        print ('time used is ',end-start)
    return wrapper

myfunc = timeit(myfunc)
myfunc()
执行结果为:

myfunc() called
time used is  0.0110723728723904

是否还能在简洁?可以的,使用语法糖@。

第五步,使用语法糖@降低字符输入量。

#!/usr/bin/python
import time 
def timeit(func):
    def wrapper():
        start = time.clock()
        func()
        end = time.clock()
        print ('time used is ',end-start)
    return wrapper

@timeit
def myfunc():
    print("myfunc() called")
myfunc()
执行结果为:

myfunc() called
time used is  0.013483512431895571

其作用和第四步的一样,只是用语法糖有一个限制,就是在装饰函数要在被装饰函数之前定义,不然就找不到装饰函数。如实例中所是,timeit函数要在myfunc函数之前定义。

第六步,对确定数量参数的函数进行装饰

#!/usr/bin/python
import time 
def timeit(func):
    def wrapper(a, b):
        start = time.clock()
        func(a, b)
        end = time.clock()
        print ('time used is ',end-start)
    return wrapper

@timeit
def myfunc(a, b):
    print("myfunc( %s, %s) called"%(a,b))
myfunc(1,2)
测试结果为:

myfunc( 1, 2) called
time used is  0.021083260107009123

第七步,对参数熟练不确定的函数进行装饰

#!/usr/bin/python
import time 
def timeit(func):
    def wrapper(*args, **kwargs):
        start = time.clock()
        func(*args, **kwargs)
        end = time.clock()
        print ('time used is ',end-start)
    return wrapper

@timeit
def myfunc(a, b):
    print("myfunc( %s, %s) called"%(a,b))

@timeit
def myfunc2(a, b, c):
    print("myfunc2( %s, %s, %s) called"%(a,b,c))
    
myfunc(1,2)
myfunc2(1,2,3)
运行结果为:

myfunc( 1, 2) called
time used is  0.005005443617309361
myfunc2( 1, 2, 3) called
time used is  0.0040197501857303815

第七步,让装饰器带参数

#!/usr/bin/python
import time

def timeit(arg):
    def _wrapper(func):
        def wrapper(*args, **kwargs):
            start = time.clock()
            func(*args, **kwargs)
            end = time.clock()
            print ('time used is ',end-start, arg)
        return wrapper
    return _wrapper

@timeit("first time")
def myfunc(a, b):
    print("myfunc( %s, %s) called"%(a,b))

@timeit("second time")
def myfunc2(a, b, c):
    print("myfunc( %s, %s, %s) called"%(a,b,c))
    
myfunc(1,2)
myfunc2(1,2,3)

测试结果:

myfunc( 1, 2) called
time used is  0.01058011828321179 first time
myfunc( 1, 2, 3) called
time used is  0.005932319138874216 second time

第八步,python内置的装饰器,staticmethod、classmethod和property


 functools模块提供了两个装饰器。这个模块是Python 2.5后新增的。

    wraps(wrapped[, assigned][, updated]): 
    这是一个很有用的装饰器。函数是有几个特殊属性比如函数名,在被装饰后,上例中的函数名bar会变成包装函数的名字wrap,如果你希望使用反射,可能会导致意外的结果。这个装饰器可以解决这个问题,它能将装饰过的函数的特殊属性保留。


http://www.cnblogs.com/rollenholt/archive/2012/05/02/2479833.html

http://my.oschina.net/shniu/blog/215365?p=1

http://www.cnblogs.com/rhcad/archive/2011/12/21/2295507.html





Logo

CSDN联合极客时间,共同打造面向开发者的精品内容学习社区,助力成长!

更多推荐