目录
引入
一个加法函数,想增强它的功能,能够输出被调用过以及调用的参数信息
def add(x, y): return x + y
用嵌套函数、柯里化来解决
def add(x,y): return x + y def logger(fn): def _loger(*args,**kwargs): print("函数开始运行") ret = fn(*args,**kwargs) print("函数结束") return ret return _loger print(logger(add)(10,40))
在变形【这就是装饰器函数,侵入式代码】
- 装饰器语法糖
def logger(fn): def _loger(*args,**kwargs): print("函数开始运行") ret = fn(*args,**kwargs) print("函数结束") return ret return _loger @logger #等价于 add1 = logger(add1) def add1(x,y): return x + y #add1 = logger(add1) print(add1(5,10))
装饰器【无参】
装饰器(无参)
- 它是一个函数
- 函数作为它的形参
- 返回值也是一个函数
- 可以使用@functionname方式,简化调用方式
装饰器和高阶函数
- 装饰器是高阶函数,但装饰器是对传入函数的功能的装饰(功能增强)
文档字符串
Python的文档
- Python是文档字符串Documentation Strings
- 在函数语句块的第一行,且习惯是多行的文本,所以多使用三引号
- 惯例是首字母大写,第一行写概述,空一行,第三行写详细描述
- 可以使用特殊属性__doc__访问这个文档
只能放在番薯的第一行,否则无法获取到,显示为None
def add(x,y): """ This a funation add :param x: :param y: :return: """ return x + y print("name={},\ndoc={}".format(add.__name__,add.__doc__)) name=add, doc= This a funation add :param x: :param y: :return:
怎样才能使用,@logger装饰器的函数之后,显示add原函数的doc文档信息??
def logger(fn): def _loger(*args,**kwargs): "This funation logger" print("函数开始运行") ret = fn(*args,**kwargs) print("函数结束") return ret return _loger @logger def add(x,y): ''' This is funation add ''' return x + y print(add.__name__,add.__doc__) 结果: _loger This funation logger
变法一:自己编写函数去实现
- 通过copy_properties函数将被包装函数的属性覆盖掉包装函数
- 凡是被装饰的函数都需要复制这些属性,这个函数很通用
- 可以将复制属性的函数构建成装饰器函数,带参装饰器
def copy_doc(src,dst): dst.__name__ = src.__name__ dst.__doc__ = src.__doc__ def logger(fn): def loger(*args,**kwargs): "This funation logger" print("函数开始运行") ret = fn(*args,**kwargs) print("函数结束") return ret copy_doc(fn,loger) return loger @logger def add(x,y): ''' This is funation add ''' return x + y print(add(3,6),add.__name__,add.__doc__) 结果: 函数开始运行 函数结束 9 add This is funation add
变法二:进一步改变为装饰器函数
def copy_doc(src): def _copy(dst): dst.__name__ = src.__name__ dst.__doc__ = src.__doc__ return dst return _copy def logger(fn): @copy_doc(fn) #copy_doc(fn,loger) def loger(*args,**kwargs): "This funation logger" print("函数开始运行") ret = fn(*args,**kwargs) print("函数结束") return ret return loger @logger def add(x,y): ''' This is funation add ''' return x + y print(add(3,6),add.__name__,add.__doc__) 结果: 函数开始运行 函数结束 9 add This is funation add
带参装饰器
带参装饰器
- 函数作为它的形参
- 返回值是一个不带参的装饰器函数
- 使用@functionname(参数列表)方式调用
- 可以看做在装饰器外层又加了一层函数
例子:计算函数运行行多长时间
import datetime import time def logger(fn): def wrap(*args, **kwargs): # before 功能增强 print("args={}, kwargs={}".format(args,kwargs)) start = datetime.datetime.now() ret = fn(*args, **kwargs) # after 功能增强 duration = (datetime.datetime.now()-start).total_seconds() print("function {} took {}s.".format(fn.__name__, duration)) return ret return wrap @logger # 相当于 add = logger(add) def add(x, y): print("===call add===========") time.sleep(2) return x + y print(add(4, y=7)) 结果: args=(4,), kwargs={'y': 7} ===call add=========== function add took 2.007478s. 11
上面的例子,不够灵活,如果我们计算,大于3秒,小于5秒的函数,改怎么来改
- 时间自己来控制
import datetime import time def logger(t1,t2): def t_time(fn): def wrap(*args, **kwargs): # before 功能增强 print("args={}, kwargs={}".format(args,kwargs)) start = datetime.datetime.now() ret = fn(*args, **kwargs) # after 功能增强 duration = (datetime.datetime.now()-start).total_seconds() if duration >t1 and duration < t2: print("function {} took {}s.".format(fn.__name__, duration)) return ret return wrap return t_time @logger(3,5) # 相当于 add = logger(add) def add(x, y): print("===call add===========") time.sleep(4) return x + y print(add(4, y=7)) 结果: args=(4,), kwargs={'y': 7} ===call add=========== function add took 4.002905s. 11
总结
最难的装饰器也就是上面个这个样子了
将记录的功能提取出来,这样就可以通过外部提供的函数来灵活的控制输出
import datetime
def copy_doc(src):
def _copy(dst):
dst.__name__ = src.__name__
dst.__doc__ = src.__doc__
return dst
return _copy
def logger(duration, func=lambda name, duration: print('{} took {}s'.format(name, duration))):
def _logger(fn):
@copy_doc(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__, duration)
return ret
return wrapper
return _logger