defadd(x, y):return x + y
deflogger(fn):
print('begin') # 增强的输出
x = fn(4, 5)
print('end') # 增强的功能return x
print(logger(add))
----------------------------
begin
end
9
改进需求,传参
解决了传参的问题,进一步改变
defadd(x, y):return x + y
deflogger(fn, *args, **kwargs):
print('begin')
x = fn(*args, **kwargs)
print('end')
return x
print(logger(add, 4, y=5))
--------------------------------
begin
end
9
改进需求,柯里化
defadd(x, y):return x + y
deflogger(fn):def_logger(*args, **kwargs):
print('begin')
x = fn(*args, **kwargs)
print('end')
return x
return _logger
print(logger(add)(5, y=50))
# 可以换一种写法
add = logger(add) # 内层函数_logger,先计算=右边,add1重新赋值并定义,fn已经闭包被执行
print(add(x=5, y=10)) # logger(add, x=5, y=10)
装饰器语法糖
deflogger(fn):defwrapper(*args, **kwargs):
print('begin')
x = fn(*args, **kwargs)
print('end')
return x
return wrapper
@logger # 等价于 add = logger(add)defadd(x, y):return x + y
print(add(45, 40))
@logger是什么?这就是装饰器语法
装饰器说明
装饰器(无参)
它是一个函数
函数作为它的形参
返回值也是一个函数
可以使用@functionname方式,简化调用
装饰器和高阶函数
装饰器是高阶函数,但装饰器是对传入函数的功能的装饰(功能增强)
代码演示
import datetime
import time
deflogger(fn):defwrap(*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 wrap
@logger # 相当于 add = logger(add)defadd(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.016632s. # 注意:这里显示的函数是add11
怎样理解装饰器呢?
画框
装饰器函数
玻璃
前置功能增强
画
被增强函数
这个是核心
背板
后置功能增强
文档字符串
Python的文档
Python是文档字符串Documentation Strings
在函数语句块的第一行,且习惯是多行的文本,所以多使用三引号
惯例是首字母大写,第一行写概述,空一行,第三行写详细描述
可以使用特殊属性doc访问这个文档
defadd(x, y):"""This is a function of addition"""
a = x + y
return x + y
print("name={}\ndoc={}".format(add.__name__, add.__doc__))
print("====================")
print(help(add))
---------------------------------------------------------
name=add
doc=This is a function of addition
====================
Help on function add in module __main__:
add(x, y)
This is a function of addition
None
装饰器副作用
deflogger(fn):defwrapper(*args, **kwargs):"""I am wrapper"""
print("begin")
x = fn(*args, **kwargs)
print("end")
return x
return wrapper
@logger # add = logger(add)defadd(x, y):"""This is a function for add"""return x + y
print("name={}\ndoc={}".format(add.__name__, add.__doc__))
-------------------------------------------
name=wrapper
doc=I am wrapper
原函数对象的属性都被替换了,而使用装饰器,我们的需求是查看被封装函数的属性,如何解决?
包装函数属性
提供一个函数,被封装函数属性 ==copy==> 包装函数属性
defcopy_properties(src, dst):# 被封装函数属性 ==copy==> 包装函数属性(理解成把外层的函数属性传给内层函数属性)
dst.__name__ = src.__name__
dst.__doc__ = src.__doc__
deflogger(fn):defwrapper(*args, **kwargs):"""I am wrapper"""
print("begin")
x = fn(*args, **kwargs)
print("end")
return x
copy_properties(fn, wrapper) # fn对应的是src,wrapper对应的是dstreturn wrapper
@loggerdefadd(x, y):"""This is a function for add"""return x + y
print("name={}\ndoc={}".format(add.__name__, add.__doc__))
-------------------------------------
name=add
doc=This is a function for add
包装函数属性说明
通过copy_properties函数将被包装函数的属性覆盖掉包装函数
凡是被装饰的函数都需要复制这些属性,这个函数很通用
可以将复制属性的函数构建成装饰器函数,带参装饰器
包装函数柯里化
提供一个函数,被封装函数属性 ==copy==> 包装函数属性,改造成带参装饰器
defcopy_properties(src):# 柯里化def_copy(dst):# 被封装函数属性 ==copy==> 包装函数属性
dst.__name__ = src.__name__
dst.__doc__ = src.__doc__
return dst
return _copy
deflogger(fn):@copy_properties(fn) # wrapper = copy_properties(fn, wrapper)defwrapper(*args, **kwargs):"""I am wrapper"""
print("begin")
x = fn(*args, **kwargs)
print("end")
return x
return wrapper
@logger # add = logger(add)defadd(x, y):"""This is a function for add"""return x + y
print("name={}\ndoc={}".format(add.__name__, add.__doc__))
---------------------------------------------
name=add
doc=This is a function for add
带参装饰器
需求:获取函数的执行时长,对时长超过阈值的函数记录一下
import datetime
import time
defcopy_properties(src):# 柯里化def_copy(dst):# 被封装函数属性 ==copy==> 包装函数属性
dst.__name__ = src.__name__
dst.__doc__ = src.__doc__
return dst
return _copy
deflogger(duration):def_logger(fn):@copy_properties(fn) # wrapper = copy_properties(fn, wrapper)defwrapper(*args, **kwargs):"""I am wrapper"""
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
@logger(5) # add = logger(5)(add)defadd(x, y):"""This is a function for add"""
time.sleep(3)
return x + y
print(add(5, 6))
-----------------------------------------
so fast
11
带参装饰器总结
它是一个函数
函数作为它的形参
返回值是一个不带参的装饰器函数
使用@functionname(参数列表)方式调用
可以看做在装饰器外层又加了一层函数
带参装饰器灵活控制
将记录的功能提取出来,这样就可以通过外部提供的函数来灵活的控制输出
import datetime
import time
defcopy_properties(src):# 柯里化def_copy(dst):# 被封装函数属性 ==copy==> 包装函数属性
dst.__name__ = src.__name__
dst.__doc__ = src.__doc__
return dst
return _copy
deflogger(duration,
func=lambda name,
duration:print("{} took {}s".format(name, duration))):def_logger(fn):@copy_properties(fn) # wrapper = copy_properties(fn, wrapper)defwrapper(*args, **kwargs):"""I am wrapper"""
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
@logger(2) # add = logger(2)(add)defadd(x, y):"""This is a function for add"""
time.sleep(3)
return x + y
print(add(5, 6))
--------------------------------------
add took 2s
11