python 装饰器
需求
- 一个加法函数,想增强它的功能,能够输出被调用过以及调用过参数的信息
def add1(x,y): print("call add, x+y") #日志输出的控制台 return x+y
以上函数完成了需求,但是存在以下缺点:
- 打印语法的耦合太高
- 加法函数属于业务功能,而输出信息的功能,属于非业务功能代码,不该放在业务函数加法中
业务功能分离
def add1(x,y): return x+y def logger(fn): print("begin") x = fn(4,5) print("end") return x print(logger(add1))
但这里fn调用传参是个问题
解决传参的问题
def add1(x,y): return x+y def logger(fn,*args,**kwargs): print("begin") x = fn(*args,**kwargs) #这里是在解构 print("end") return x print(logger(add1,5,y=10))
柯里化
def add(x,y): return x+y def logger(fn): def wrapper(*args,**kwargs): print("begin") x = fn(*args,**kwargs) #这里是在解构 print("end") return x return wrapper print(logger(add)(5,y = 10))
装饰器语法糖
def logger(fn): def wrapper(*args,**kwargs): print("begin") x = fn(*args,**kwargs) #这里是在解构 print("end") return x return wrapper @logger def add(x,y): return x+y print(add(45,500))
@logger是装饰器的语法
装饰器(无惨)
- 是一个函数
- 函数作为它的参数
- 返回值也是一个函数
- 可以使用@functionname方式,简化调用
装饰器和高阶函数
- 装饰器是高阶函数,但装饰器是对传入函数的功能是装饰(增强)
import datetime import time def logger(fn): def wrapper(*args,**kwargs): print("begin") print("args={},kwargs={}".format(args,kwargs)) start = datetime.datetime.now() x = fn(*args,**kwargs) #这里是在解构 duration = datetime.datetime.now() -start print("function {} took {}s.".format(fn.__name__,duration.total_seconds())) return x return wrapper @logger # add = logger(add) def add(x,y): print("==========call add==========") time.sleep(2) return x+y print(add(45,500))
Python 文档字符串
python文档
- python文档字符串Documentation Strings
- 在函数语句块的第一行,且习惯是多行的文本,所以多引用三引号
- 惯例是首字符大写,第一行写概述,空一行,第三行写详细描述
- 可以使用特殊属性__doc__访问该文档
def add(x,y): """This is function of addition""" a = x+y return x+y print("name={}\ndoc={}".format(add.__name__,add.__doc__)) print(help(add))
副作用出现的问题:
def logger(fn): def wrapper(*args,**kwargs): "I am a 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__))
原函数对象的属性都被替换了,而使用装饰器,我们的需求是查看被封装函数的属性,如何解决呢?
提供一个函数,被封装函数属性==copy==>包装函数属性
def copy_properties(src,dest): #可以改造成装饰器 dest.__name__ = src.__name__ dest.__doc__ = src.__doc__ def logger(fn): def wrapper(*args,**kwargs): "I am a 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函数将被包装函数的属性覆盖掉包装函数
- 凡是被装饰的函数都需要复制这些属性,这个函数很通用
- 可以将复制属性的函数构建成装饰器函数,带参装饰器
改造成带参装饰器
def copy_properties(src):#可以改造成装饰器 def _copy_properties(dest): dest.__name__ = src.__name__ dest.__doc__ = src.__doc__ return dest return _copy_properties def logger(fn): @copy_properties(fn) #wrapper = copy_properties(fn)(wrapper) def wrapper(*args,**kwargs): "I am a 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__))
获取函数执行时长,对时长超过阈值的函数记录一下
import datetime import time def copy_properties(src):#可以改造成装饰器 def _copy_properties(dest): dest.__name__ = src.__name__ dest.__doc__ = src.__doc__ return dest return _copy_properties def loger(duration): def _logger(fn): @copy_properties(fn) def wrapper(*args,**kwargs): start = datetime.datetime.now() x = fn(*args, **kwargs) delta=(datetime.datetime.now()-start).total_seconds() print('so slow') if delta > duration else print('so fast') return x return wrapper return _logger @loger(5) def add(x,y): time.sleep(6) return x + y print(add(10,24))
带参装饰器:
- 它是一个函数
- 函数作为它的形参
- 返回值是一个不带参的装饰器函数
- 使用@functionname(参数列表)方式调用
- 可以看做在装饰器外层又加了一层函数
打印功能提出来
import datetime import time def copy_properties(src):#可以改造成装饰器 add def _copy_properties(dest): #wrapper dest.__name__ = src.__name__ dest.__doc__ = src.__doc__ return dest return _copy_properties def loger(duration,func=lambda name,duration:print('{} took {}s'.format(name,duration))): #loger(5)(add) duration=5 def _logger(fn): #loger(5)(add) fn = add @copy_properties(fn) #wrapper = _copy_properties(fn)(wrapper) def wrapper(*args,**kwargs): start = datetime.datetime.now() x = fn(*args, **kwargs) delta=(datetime.datetime.now()-start).total_seconds() if delta > duration: func(fn.__name__,duration) return x return wrapper return _logger @loger(5) #add = loger(5)(add) def add(x,y): time.sleep(6) return x + y print(add(10,24))
functools模块
functools.update_wrapper(wrapper, wrapped, assigned=WRAPPER_ASSIGNMENTS, updated=WRAPPER_UPDATES)
- 类似copy_properties功能
- wrapper 包装函数,被更新者,wrapped 被包装函数,数据源
- 元组WRAPPER_ASSIGNMENTS中是要被覆盖的属性,'__module__', '__name__', '__qualname__', '__doc__', '__annotations__' 模块名、名称、限定名、文档、参数注解
- 元组WRAPPER_UPDATES中是要被更新的属性,__dict__属性字典
- 增加一个__warpped__属性,保留着warpped函数
import datetime, time, functools def logger(duration, func=lambda name, duration: print('{} took {}s'.format(name, duration))): def _logger(fn): 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 functools.update_wrapper(wrapper, fn) return _logger @logger(5) # add = logger(5)(add) def add(x,y): time.sleep(1) return x + y print(add(5, 6), add.__name__, add.__wrapped__, add.__dict__, sep='\n')
@functools.wraps(wrapped, assigned=WRAPPER_ASSIGNMENTS, updated=WRAPPER_UPDATES)
- 类似copy_properties功能
- wrapped 被包装函数
- 元组WRAPPER_ASSIGNMENTS中是要被覆盖的属性,'__module__', '__name__', '__qualname__', '__doc__', '__annotations__' 模块名、名称、限定名、文档、参数注解
- 元组WRAPPER_UPDATES中是要被更新的属性,__dict__属性字典
- 增加一个__warpped__属性,保留着warpped函数
import datetime, time, functools def logger(duration, func=lambda name, duration: print('{} took {}s'.format(name, duration))): def _logger(fn): @functools.wraps(fn) 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 @logger(5) # add = logger(5)(add) def add(x,y): time.sleep(1) return x + y print(add(5, 6), add.__name__, add.__wrapped__, add.__dict__, sep='\n')
碎片化时间学习和你一起终身学习