本节内容要先看 python闭包
装饰器,英文:decorator
作用
在 不改动源代码 的情况下,在执行函数 前和后 增加额外 功能
好处
将与函数功能 无关的代码, 抽离出来 ,封装成装饰器
在JAVA等语言中也有类似的功能,称之为 aop ,Aspect Oriented Programming 的缩写,意为:面向切面编程
装饰器 (decorator)是23种 设计模式 之一
应用场景
- 打印日志
- 性能测试
- 权限校验等
- 数据库事务处理
实现原理
本质上是闭包
实现
装饰器的原理就是 闭包
#传入函数对象func
def 装饰器名(func):
#定义内部函数,不定长参数,关键字参数
def wrapper(*args,**kwargs):
#执行传入的函数对象
ret = func(*args,**kwargs)
#返回结果
return ret
#返回内部函数
return wrapper
总结:
- 定义外部函数,形参是 函数对象
- 定义内部函数,形参是 不定长参数,关键字参数,并 返回内部函数
- 内部函数调用传入的函数对象,并将 不定长参数,关键字参数 传入
- 将函数对象执行的结果返回
通过调用函数执行
定义被测试的函数,并通过 高阶函数 执行,代码如下:
#定义函数
def test(args):
pass
#通过调用函数执行
装饰器名(test)(123)
执行过程:
- 执行 装饰器 函数,并将test函数对象传入,并返回wrapper函数对象
- wrapper函数对象后面是(123),表示执行wrapper函数,并传入123
- 在wrapper函数中执行传入的test函数,并向test函数传入参数123
- 将test函数执行的结果返回
通过@装饰器执行
@装饰器名
def test(args):
pass
test(123)
在test()函数上面加上@装饰器名
执行过程:
同上
打印日志的装饰器
在执行函数之前,打印日志,日志格式如下:
----------- 开始执行 login(), 参数: 传入的参数
定义 装饰器log (其实就是高阶函数)
def log(func):
def wrapper(*args, **kw):
print('-'*20,'开始执行 %s(),' % func.__name__,'参数:',args)
result= func(*args, **kw)
return result
return wrapper
通过调用函数执行
定义被测试的函数,并通过 高阶函数 执行,代码如下:
def login(username,password):
print("用户名:%s,密码:%s"%(username,password))
log(login)('admin','123456')
执行过程:
- 执行log()高阶函数,并将login函数对象传入,并返回wrapper函数对象
- 由于后面是(‘admin’,’123456’),会直接运行wrapper函数,并传入admin和123456
- 在wrapper函数中执行打印,然后执行传入的login函数,并向login函数传入参数
- 将login函数执行的结果返回
通过@装饰器执行
定义被测试的函数,并通过 装饰器 执行,代码如下:
@log
def login(username,password):
print("用户名:%s,密码:%s"%(username,password))
login('admin','123456')
在login()函数上面加上@log即可
执行过程:
同上
关于函数名
在使用装饰器时,如果打印函数对象,如下:
print(login.__name__)
其结果是wrapper,而不是login,因为返回的是wrapper函数对象。在此处虽然不影响执行结果,但在使用函数签名,获取函数形参等信息时,会引起bug
通过pthon内置的 functools.wraps() 装饰器实现改函数名的操作,放在 最里面内部函数的上面,改造如下:
import functools
def log(func):
@functools.wraps(func)
def wrapper(*args, **kw):
print('-'*20,'开始执行 %s(),' % func.__name__,'参数:',args)
result= func(*args, **kw)
return result
return wrapper
测试调用
print(log(login).__name__)
返回函数名为login
练习
测试函数性能
在编码中,要测试函数性能,比如不能超过5秒钟,实现原理如下:
- 在函数的开始处,获取当前时间
- 在函数的末尾处,获取当前时间
- 末尾时间减去开始时间,得到的秒数就是该函数执行的总时长
代码如下:
import datetime
import time
def login(username,password):
t=datetime.datetime.now()
result=None
if username=='admin' and password=='123456':
result=True
else:
result=False
#代码太少,间隔时间为0,所以人为休眠
time.sleep(1)
t2=datetime.datetime.now()
print("总时长:%s"%(t2-t).total_seconds())
return result
result=login('admin','123456')
用装饰器实现测试函数性能
实现性能装饰器,思路:在函数执行前记录时间,在函数执行后再次记录时间,第二个时间减去前一个时间,间隔的秒数就是该函数的执行时间
#定义装饰器
def testtime(func):
def wrapper(*args, **kw):
starttime=datetime.datetime.now()
fun= func(*args, **kw)
endtime=datetime.datetime.now()
print('-'*20,(endtime-starttime).total_seconds(),'-'*20,'结束执行 %s():' % func.__name__)
return fun
return wrapper
@testtime
def login(username,password):
print("用户名:%s,密码:%s"%(username,password))
time.sleep(0.1)
#调用执行
login('aaa','bbb')