先来看一段代码:
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.以上代码大部分摘自慕课网。