装饰器(Decorator)
装饰器推导过程
- 需求
- 一个加法函数,想增强它的功能,能够输出被调用过以及调用的参数信息
def add(x,y): return x + y
- 增加信息输出功能
def add(x,y): print('call {},{}+{}'.format(add.__name__, x, y)) return x + y
- 增加信息输出功能
- 不足:
- 打印语句的耦合太高
- 加法函数属于业务功能,而输出信息的功能,属于非业务功能代码,不该放在业务函数加法中
- 业务功能分离
def add(x,y): return x+ydef logger(fn): print('begin') #增强的输出 ret = fn(4,7) print('end') #增强的功能 return retlogger(add)
- 但是fn函数传参是一个问题
- 解决传参
def add(x,y): return x+ydef logger(fn,*args, **kwargs): print('begin') ret = fn(*args, **kwargs) print('end') return retlogger(add,7,9)
- 但又想进一步的简化
- 柯里化
def add(x,y): return x+y def logger(fn): def wrapper(*args, **kwargs): print('begin') ret = fn(*args, **kwargs) print('end') return ret return wrapper logger(add)(6,5)
- 调用可以换一种写法
add = logger(add) add(6,5)
- 调用可以换一种写法
- 装饰器语法糖
def logger(fn): def wrapper(*args, **kwargs): print('begin') x = fn(*args, **kwargs) print('end') return x return wrapper```@logger #等价于 add = logger(add)```def add(x,y): return x+yadd(6,43)
- 一个加法函数,想增强它的功能,能够输出被调用过以及调用的参数信息
无参装饰器
- 装饰器(无参)
- 它是一个函数
- 函数作为它的形参
- 返回值也是一个函数
- 可以使用@functionname方式,简化调用
- 装饰器和高阶函数
- 装饰器是高阶函数,传入和输出都是函数。
- 但装饰器是对传入函数的功能装饰(功能增强)
应用
获取函数的执行时长,对时长超过阈[yù]值的函数记录一下
import datetime
import time
def logger(fn):
def wrapper(*args, **kwargs):
start = datetime.datetime.now()
print('begin')
ret = fn(*args, **kwargs)
print('end')
end = (datetime.datetime.now() - start).total_seconds()
print('function {} took {}s'.format(fn.__name__,end))
return ret
return wrapper
@logger
def add(x,y):
print('===call add=========')
time.sleep(3)
return x+y
文档字符串
- python的文档
- python是文档字符串Documentation Strings
- 在函数语句块的第一行,且习惯是多行的文本,所以多使用三引号
- 惯例是首字母大写,第一行写概述,空一行,第三行写详细描述
- 可以使用特殊属性doc访问这个文档
def add(x,y):
"""
This is a function of addition
int x
int y
return int
"""
a = x+y
return x + y
print("name={}\ndoc={}".format(add.__name__, add.__doc__))
print(help(add))
文档字符串应用
凡是被装饰的函数都需要复制这些属性,这个函数很通用
- 副作用
def logger(fn): def wrap(*args,**kwargs): ''' This is a wrapper ''' print('begin') ret = fn(*args,**kwargs) print('end') return ret return wrap @logger def add(x,y): ''' This is a function of addition int x int y return int ''' return x + y print('name = {}, doc = {}'.format(add.__name__,add.__doc__))
- 原函数对象的属性都被替换了,而使用装饰器,我们的需求是查看被封装函数的属性
- 也就是想看到add的属性,结果显示的是wrap的
- 如何解决?
- 提供一个函数,被封装函数属性 copy 成 包装函数属性
def copy_properties(src,dst): dst.__name__ = src.__name__ dst.__doc__ = src.__doc__ def logger(fn): def wrap(*args,**kwargs): ''' This is a wrapper ''' print('begin') ret = fn(*args,**kwargs) print('end') return ret copy_properties(fn,wrap) return wrap @logger def add(x,y): ''' This is a function of addition int x int y return int ''' return x + y print('name = {}, doc = {}'.format(add.__name__,add.__doc__))
- copy_properties可以改造成装饰器
def copy_properties(src): def _copy(dst): dst.__name__ = src.__name__ dst.__doc__ = src.__doc__ return dst return _copydef logger(fn): @copy_properties(fn) def wrap(*args,**kwargs): ''' This is a wrapper ''' print('begin') ret = fn(*args,**kwargs) print('end') return ret return wrap@loggerdef add(x,y): ''' This is a function of addition int x int y return int ''' return x + yprint('name = {}, doc = {}'.format(add.__name__,add.__doc__))#第9行等价于 wrap = copy_properties(fn)(wrap)
带参装饰器
- 它是一个函数
- 函数作为它的形参
- 返回值是一个不带参的装饰器函数
- 使用@functionname(参数列表)方式,简化调用
- 可以看做在装饰器外层又加了一层函数
应用
获取函数的执行时长,对时长超过阈[yù]值的函数记录一下
import datetime
import time
def logger(t1,t2):
def _logger(fn):
def wrapper(*args, **kwargs):
start = datetime.datetime.now()
print('begin')
ret = fn(*args, **kwargs)
print('end')
delta = (datetime.datetime.now() - start).total_seconds()
if delta > t1 and delta < t2:
print('function {} took {}s'.format(fn.__name__,delta)
return ret
return wrapper
return _logger
@logger(3,5)
def add(x,y):
print('===call add=========')
time.sleep(6)
return x+y
- 将记录的功能提取出来,这样就可以通过外部提供的函数来灵活的控制输出
import datetime import time def logger(duration, func = lambda name,t1: print('{} took {}s'.format(name,t1))): def _logger(fn): def wrapper(*args, **kwargs): start = datetime.datetime.now() print('begin') ret = fn(*args, **kwargs) print('end') delta = (datetime.datetime.now() - start).total_seconds() if delta > duration: func(fn.__name__, delta) return ret return wrapper return _logger @logger(3) def add(x,y): print('===call add=========') time.sleep(6) return x+y
本文来自投稿,不代表Linux运维部落立场,如若转载,请注明出处:http://www.178linux.com/88024