装饰器
需求:
一个加法函数,想增强它的功能,能够输出被调用过以及调用的参数信息
def add(x,y):
return x+y
增加信息输出功能:
def add(x,y):
print('call add,x+y') #日志输出到控制台
return x+y
上面的加法函数是完成了需求,但是有一下的缺点:
打印是一个功能,这条语句和add函数耦合太高
加法函数属于业务功能,而输出信息的功能,属于非业务功能代码,不该放在业务函数add中
下面代码做到了业务功能分离,但是fn函数调用传参是个问题:
def add(x,y):
return x + y
def logger(fn):
print('begin') #增强的输出
x = fn(4,5)
print('end')
return x
print(logger(add))
解决了传参的问题,进一步改变
def add(x,y):
return x + y
def logger(fn,*args,**kwargs):
print('begin')
x = fn(*args,**kwargs)
print('end')
return x
print(logger(add,5,y=60))
柯里化:
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
add = logger(add)
print(add(x=5,y=10))
装饰器语法糖:
@logger
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
装饰器和高阶函数
装饰器可以是高阶函数,但装饰器是对传入函数的功能的装饰(功能增强)
def logger(fn):
def wrapper(*args,**kwargs):
print("args={},kwargs={}".format(args,kwargs))
start = datetime.datetime.now()
ret = fn(*args,**kwargs)
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('====')
time.sleep(2)
return x+y
print(add(4,7))
装饰器相当于在不修改当前代码的情况下,增加原有代码的功能
Python的文档
Python文档字符串Documentation Strings
在函数语句块的第一行,且习惯是多行的文本,所以多使用三引号
惯例是首字母大写,第一行写概述,空一行,第三行写详细描述
可以使用特殊属性_doc_访问这个文档
def add(x,y):
"""This is a function of addition"""
return x + y
add.__doc__
'This is a function of addition'
help(add)
Help on function add in module __main__:
add(x, y)
This is a function of addition
def logger(fn):
def wrapper(*args,**kwargs):
print('begin')
x = fn(*args,**kwargs)
print('end')
return x
return wrapper
@logger
def add(x,y):
"""This is a function of addition"""
return x + y
print("name = {},doc = {}".format(add.__name__,add.__doc__))
name = wrapper,doc = None
提供一个函数,被封装函数属性 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
def add(x,y):
"""This is a function of addition"""
return x + y
print("name = {},doc = {}".format(add.__name__,add.__doc__))
name = wrapper,doc = This is a function of addition
通过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)
def wrapper(*args,**kwargs):
'I am wrapper'
print('begin')
x = fn(*args,**kwargs)
print('end')
return x
return wrapper
@logger
def add(x,y):
"""This is a function of addition"""
return x + y
print("name = {},doc = {}".format(add.__name__,add.__doc__))
name = wrapper,doc = This is a function of addition
需求:获取函数的执行时长,对时长超过阈值的函数记录一下
from datetime import timedelta
import datetime
def logger(duration):
def _logger(fn):
@copy_properties(fn)
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(*args,**kwargs)
def add(x,y):
time.sleep(3)
return x + y
print(add(5,6))
so fast
11
带参装饰器
它是一个函数
函数作为它的形参
返回值是一个不带参的装饰器函数
使用@functionname(参数列表)方式调用
可以看做在装饰器外层又加上了一层函数,这个函数可以多参数
from datetime import timedelta
import datetime
def logger(duration,func=lambda name,delta:print('{} took {:.2f}s'.format(name,delta))):
def _logger(fn):
@copy_properties(fn)
def wrapper(*args,**kwargs):
start = datetime.datetime.now()
ret = fn(*args,**kwargs)
delta = (datetime.datetime.now() - start).total_seconds()
print(func)
if delta > duration:
func(fn.__name__ ,delta)
return ret
return wrapper
return _logger
@logger(2) #add = logger(5)(add) = _logger(add) = wrapper(*args,**kwargs)
def add(x,y):
time.sleep(3)
return x + y
print(add(5,6))
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_属性字典
增加一个_wrapped_属性,保留着wrapped函数
@functools.wraps(wrapped,assigned = WRAPPER_ASSIGNMENTS,updated=WRAPPER_UPDATES)
类似copy_properties功能
wrapped被包装函数
元组WRAPPER_ASSIGNMENTS中是要被覆盖的属性
‘module’,‘name’,‘qualname’,‘doc’,'annotations’模块名、名称、限定名、文档、参数注解
元祖WRAPPER_UPDATES中是要被更新的属性,_dict_属性字典
增加一个_wrapped_属性,保留着wrapped函数
from datetime import timedelta
import datetime
import functools
def logger(duration,func=lambda name,delta:print('{} took {:.2f}s'.format(name,delta))):
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__ ,delta)
return ret
return wrapper
return _logger
@logger(2) #add = logger(5)(add) = _logger(add) = wrapper(*args,**kwargs)
def add(x,y):
time.sleep(3)
return x + y
print(add(5,6))