1.装饰器介绍
add函数
>>> def add(x,y):
... return x+y
>>> print(add(4,4))
8
变式一:增加打印日志到控制台
>>> def add(x,y):
... print("call add,x+y")
... return x+y
>>> print(add(4,4))
call add,x+y
8
变式二:解决变式一耦合度太高问题
>>> def add(x,y):
... return x+y
>>> def logger(fn):
... print('begin')
... ret = fn(4,5)
... print('end')
... return ret
>>> print(logger(add))
begin
end
9
变式三:解决变式二传参传递问题
>>> def add(x,y):
... return x+y
>>> def logger(fn,*args,**kwargs):
... print("begin")
... ret = fn(*args,**kwargs)
... print("end")
... return ret
>>> print(logger(add,5,y=100))
begin
end
105
变式四:解决变式三中logger函数进行柯里化
>>> 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
>>> print(logger(add)(11,y=24))
begin
end
35
>>> add = logger(add) # 如果使用这一个赋值方式,需要在add函数和logger函数调用完成之后才可以
>>> add(11,y=45)
begin
end
56
变式五:装饰器的语法,@functionname
>>> @logger
... def add(x,y):
... return x+y
...
>>> add(55,55)
begin
end
110
装饰器语法:
一个装饰器就是一个函数,它接受一个函数作为参数并返回一个新的函数。
可以使用@functionname方式,简化调用
装饰器就是一个高阶函数,但装饰器是对传入函数的功能的装饰(功能增强)
2.装饰器的应用(一)
统计函数运行时间
[root@node05 python]# vim test2.py
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
print("function {}took {}s.".format(fn.__name__,duration.total_seconds()))
return ret
return wrap
@logger
def add(x,y):
print("===call add===")
time.sleep(2)
return x+y
print(add(5,y=100))
[root@node05 python]# python3 test2.py
args=(5,),kwargs={'y': 100}
===call add===
function addtook 2.002801s.
105
3.创建装饰器时保留函数的元信息
>>> def logger(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 for add"""
... return x+y
>>> print("name={},doc={}".format(add.__name__,add.__doc__))
name=wrapper,doc=I am wrapper
变式1:提供一个copy_properties函数,包装本封装函数属性
通过copy_properties函数将被包装函数的属性覆盖掉包装函数
凡是被装饰的函数都需要复制这些属性,这个函数必须要通用
可以将复制属性的函数构建成装饰器函数,带参装饰器
[root@node05 python]# vim test3.py
def copy_properties(dst,src):
dst.__name__ = src.__name__
dst.__doc__ = src.__doc__
def logger(fn):
def wrapper(*args,**kwargs): # wrapper包装函数
"""I am wrapper"""
print('begin')
x = fn(*args,**kwargs) # fn称为被包装函数
print('end')
return x
# def copy_properties(dst,src):
# dst.__name__ = src.__name__
# dst.__doc__ = src.__doc__
copy_properties(wrapper,fn)
return wrapper
@logger
def add(x,y):
"""This is a function for add"""
return x+y
print("name = {},doc={}".format(add.__name__,add.__doc__))
[root@node05 python]# python3 test3.py
name = add,doc=This is a function for add
变式2:将copy_properties函数柯里化,并改造成带参装饰器
这里需要把copy_properties函数的src和dst顺序进行修改,更加便于改写为带参装饰器
[root@node05 python]# vim test3.py
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) # wrapper = copy_properties(fn)(wrapper)
def wrapper(*args,**kwargs): # wrapper包装函数
"""I am wrapper"""
print('begin')
x = fn(*args,**kwargs) # fn称为被包装函数
print('end')
return x
return wrapper
@logger
def add(x,y):
"""This is a function for add"""
return x+y
print("name = {},doc={}".format(add.__name__,add.__doc__))
[root@node05 python]# python3 test3.py
name = add,doc=This is a function for add
4.装饰器的应用(二)
获取函数执行时长,对时长超过阈值的函数进行记录
[root@node05 python]# vim test2.py
import datetime
import time
def copy_properties(src):
def _copy(dst):
dst.__name__ = src.__name__
dst.__doc__ = src.__doc__
return dst
return _copy
def logger(duration):
def _logger(fn):
@copy_properties(fn) # wrapper = wrapper(fn)(wrapper)
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)
def add(x,y):
time.sleep(3)
return x + y
[root@node05 python]# python3 test2.py
so fast
11
5.带参装饰器
也是一个函数,它的形参也是函数,返回值是一个不带参的装饰器函数
使用@functionname(参数列表)方式调用,可以看做在装饰器外层又加一层函数
#将记录的功能提取出来,这样就可以通过外部提供的函数来灵活的控制输出
import datetime
import time
def copy_properties(src):
def _copy_properties(dst):
dst.__name__ = src.__name__
dst.__doc__ = src.__doc__
return dst
return _copy_properties
def logger(duration,func=lambda name,delta:print('{} took {:2f}s'.format(name,delta))):
def _logger(fn):
@copy_properties(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__,delta)
return ret
return wrapper
return _logger
@logger(5)
def add(x,y):
time.sleep(7)
return x + y
print(add(4,5))
# 程序运行结果
add took 7.000445s
9
网友评论