作用:想要为一个函数添加新的功能,但是又不想要改变函数本身的代码。例如想要为函数添加log等功能。
def new_fn(f):
"""
给原函数添加一个log信息
:param f: 原函数
:return: 原功能+新功能的新函数
"""
def fn(x):
print('call'+f.__name__+'()') # 添加log信息
return f(x) # 调用原函数
return fn
# 添加装饰器的语法:@装饰器
@new_fn
def f(x):
return x*x
print(f(2))
无参数的装饰器:
打印出累积函数的调用时间。
import time
def performance(f):
def fn(*args,**kwargs):
t1 = time.time()
r = f(*args,**kwargs)
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)
带参数的装饰器:
打印出累积函数的调用时间,可以选择以秒或者毫秒来显示。
import time
def performance(unit):
def performance_decorator(f):
def fn(*args,**kwargs):
t1=time.time()
r = f(*args,**kwargs)
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 fn
return performance_decorator
@performance('ms')
def factorial(n):
return reduce(lambda x,y: x*y, range(1, n+1))
print factorial(10)
装饰器performance中有3层嵌套。核心的装饰器代码是中间2层,最外层是为整个装饰器新增了参数。但是单独拿出中间2层执行会报错,因为内部函数引用了外部函数的局部变量unit,也就是之前讲的闭包。由此可见,闭包内的函数不能够拆开来当做普通函数单独执行。
经过装饰器装饰过的函数返回的新函数的函数名已经不是原函数的函数名factorial了,而是装饰器内部定义的函数名fn,这对于那些依赖函数名的代码来说就会失效。需要将原函数的一些属性特性复制到新函数中,如下所示:
import time
def performance(unit):
def performance_decorator(f):
def fn(*args,**kwargs):
t1= time.time()
r = f(*args,**kwargs)
t2 = time.time()
t =(t2-t1)*1000 if unit==ms else (t2-t1)
print 'call %s() in %f %s'%(f.__name__,t,unit)
fn.__name__ = f.__name__ # 复制原函数特性到新函数
fn.__doc__ = f.__doc__ # 复制原函数特性到新函数
return fn
return performance_decorator
@performance('ms')
def factorial(n):
return reduce(lambda x,y: x*y, range(1, n+1))
print factorial.__name__
像上面一个一个复制,是不可能的,因此python提供了内置的functools来完成“复制”的工作。
import time, functools # 导入模块
def performance(unit):
def performance_decorator(f):
@functools.wraps(f) # 使用functools模块,给fn新增复制的功能。
def fn(*args,**kwargs):
t1= time.time()
r = f(*args,**kwargs)
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 fn
return performance_decorator
@performance('ms')
def factorial(n):
return reduce(lambda x,y: x*y, range(1, n+1))
print factorial.__name__ # factorial