装饰器
装饰器(无参)
它是一个函数
函数作为它的实参。无参装饰器实际上就是一个单形参函数
返回值也是一个函数
可以使用@functionname方式,简化调用
注:此处装饰器的定义只是就目前所学的总结,并不准确,只是方便理解
装饰器和高阶函数
装饰器可以是高阶函数,但装饰器是对传入函数的功能的装饰(功能增强)
装饰器例:
import datetime
import time
def logger(fn):
def wrapper(*args, **kwargs): ##括号里面为形参
# before 功能增强
print("args={}, kwargs={}".format(args,kwargs))
start = datetime.datetime.now() # 计算时长
ret = fn(*args, **kwargs) ##括号里面为参数解构
# after 功能增强
duration = datetime.datetime.now() - start
print("function {} took {}s.".format(fn.__name__, duration.total_seconds()))
return ret
return wrapper
@logger
def add(x, y):
print("===call add===========")
time.sleep(2)
return x + y
print(add(4, y=7))
@logger 是什么?这就是装饰器语法,相当于 add = logger(add) 也就相当于 = wrapper, add()就是wrapper(),调用wrappe返回fn()的返回值,而fn是调用logger(fn)的fn,用到了闭包,所以wrappe()的返回值还是add()的返回值
装饰器副作用:
def logger(fn):
def wrapper(*args,**kwargs):
'I am wrapper'
print('begin')
x = fn(*args,**kwargs)
print('end')
return x
return wrapper
@logger #add = logger(add)
def add(x,y):
'''This is a function for add'''
return x + y
print("name={}, doc={}".format(add.__name__, add.__doc__))
如上例,用logger装饰器装饰add,实际上add = logger(add) = wrapper,所以此时add.__name__是 wrapper__name__,使用装饰器,原函数对象的属性都被替换了,我们的需求是查看原函数的属性,如何解决?
提供一个函数,被封装函数属性 copy> 包装函数属性
def copy_properties(src, dst): # 可以改造成装饰器
dst.__name__ = src.__name__
dst.__doc__ = src.__doc__
def logger(fn):
def wrapper(*args,**kwargs):
'I am wrapper'
print('begin')
x = fn(*args,**kwargs)
print('end')
return x
copy_properties(fn, wrapper)
return wrapper
@logger #add = logger(add)
def add(x,y):
'''This is a function for add'''
return x + y
print("name={}, doc={}".format(add.__name__, add.__doc__))
> 通过copy_properties函数将被包装函数的属性覆盖掉包装函数
> 凡是被装饰的函数都需要复制这些属性,这个函数很通用
> 可以将复制属性的函数构建成装饰器函数,带参装饰器
提供一个函数,被封装函数属性 copy> 包装函数属性,改造成带参装饰器
def copy_properties(src): # 柯里化
def _copy(dst):
dst.__name__ = src.__name__
dst.__doc__ = src.__doc__
return dst
return _copy
def logger(fn):
@copy_properties(fn) # copy_properties(fn)=_copy,wrapper=@copy_properties(fn) (wrapper)=@_copy(wrapper)=wrapper----属于带参装饰器
def wrapper(*args,**kwargs):
'I am wrapper'
print('begin')
x = fn(*args,**kwargs)
print('end')
return x
return wrapper
@logger #add = logger(add)
def add(x,y):
'''This is a function for add'''
return x + y
print("name={}, doc={}".format(add.__name__, add.__doc__))
需求:获取函数的执行时长,对时长超过阈值的函数记录一下
def logger(duration):
def _logger(fn):
@copy_properties(fn) # wrapper = wrapper(fn)(wrapper)
def wrapper(*args,**kwargs):
start = datetime.datetime.now()
ret = fn(*args,**kwargs)
delta = (datetime.datetime.now() - start).total_seconds()
print('so slow') if delta > duration else print('so fast')
return ret
return wrapper
return _logger
@logger(5) # add = logger(5)(add)=_logger(add)=wrapper-----属于带参装饰器
def add(x,y):
time.sleep(3)
return x + y
print(add(5, 6))
带参装饰器
> 它是一个函数
> 函数作为它的形参
> 返回值是一个不带参的装饰器函数
> 使用@functionname(参数列表)方式调用
> 可以看做在装饰器外层又加了一层函数,这个函数可以多参数
> 将记录的功能提取出来,这样就可以通过外部提供的函数来灵活的控制输出
def logger(duration, func=lambda name, delta: print('{} took {:.2f}s'.format(name, delta))):-----提供多参
def _logger(fn):
@copy_properties(fn) # wrapper = wrapper(fn)(wrapper)
def wrapper(*args,**kwargs):
start = datetime.datetime.now()
ret = fn(*args,**kwargs)
delta = (datetime.datetime.now() - start).total_seconds()
if delta > duration:
func(fn.__name__, delta)
return ret
return wrapper
return _logger
使用形式@logger(duration, func=lambda name, delta: print('{} took {:.2f}s'.format(name, delta)))