回顾了廖老师的装饰器的教程,记录一下
本质上,装饰器就是一个返回函数的高阶函数。所以,我们要定义一个能打印日志的decorator,可以定义如下:
def log(func):
def wrapper(*args, **kw):
print('call %s():' % func.__name__)
return func(*args, **kw)
return wrapper
@log
def now():
print('2015-3-25')
now()
call now():
2015-3-25
把@log
放到now()
函数的定义处,相当于执行了语句:now = log(now)
由于log()是一个decorator,返回一个函数,所以,原来的now()函数仍然存在,只是现在同名的now变量指向了新的函数,于是调用now()将执行新函数,即在log()函数中返回的wrapper()函数。
如果decorator本身需要传入参数,那就需要编写一个返回decorator的高阶函数,写出来会更复杂。比如,要自定义log的文本:
def log(text):
def decorator(func):
def wrapper(*args, **kw):
print('%s %s():' % (text, func.__name__))
return func(*args, **kw)
return wrapper
return decorator
下面我们根据几个需求好好理解一下上述的执行流程。假设我们要打印一个函数的执行时间
def metric(text):
def decorator(func):
@functools.wraps(func)
def wrapper(*args,**kwargs):
t1 = time.time()
res = func(*args,**kwargs)
t2 = time.time()
print('%s executed in %s ms %s' % (func.__name__, t2-t1, text))
return res
return wrapper
return decorator
@metric
def fast(x, y, z):
time.sleep(0.0012)
return x + y;
执行上述函数,会发现报错decorator() takes 1 positional argument but 3 were given'
,这是因为带参数的装饰器执行流程为metric(text)(func)(*args,**kwargs)
,当没有传入text,metric的第一个参数变为func,decorator的参数变为(*args,**kwargs),即fast的参数,所以会报以上错误。
如何解决呢?我们加一个判断如下
def metric(text):
def decorator(func):
@functools.wraps(func)
def wrapper(*args,**kwargs):
t1 = time.time()
res = func(*args,**kwargs)
t2 = time.time()
print('%s executed in %s ms %s' % (func.__name__, t2-t1, text))
return res
return wrapper
if type(text)==str:
return decorator
else:
return decorator(text)
@metric
def fast(x, y, z):
time.sleep(0.0012)
return x + y
这样就可以满足上述不加text的情况,如果还想这样调用metric()
,可以设定一个默认值
def metric(text=""):
def decorator(func):
@functools.wraps(func)
def wrapper(*args,**kwargs):
t1 = time.time()
res = func(*args,**kwargs)
t2 = time.time()
if text:
print('%s executed in %s ms %s' % (func.__name__, t2-t1, text))
else:
print('%s executed in %s ms' % (func.__name__, t2-t1))
return res
return wrapper
if type(text)==str:
return decorator
else:
return decorator(text)
@metric()
def fast(x, y, z):
time.sleep(0.0012)
return x + y
总结一下,关键是理解函数传入的流程。