一、什么是装饰器
测试面试必问题,测试开发必修课,一文带你快速看懂!
作用:装饰器的作用就是为已经存在的对象添加额外的功能。
本质:本质上是一个 Python 函数或类,可以让其他函数或类在不需要做任何代码修改的前提下增加额外功能。
应用场景:经常用于有切面需求的场景,比如:插入日志、性能测试、事务处理、缓存、权限校验等场景。可以抽离出大量与函数功能本身无关的雷同代码到装饰器中并继续重用。
二、基础知识
Python和Java不一样,Python 中的函数可以像普通变量一样当做参数传递给另外一个函数:
def test1():print("test1")def test2(func):func()test2(test1)
场景思考:
如果需要在下面的这个函数中记录下函数的执行日志
def test1():print("test1")
可以在代码中直接添加日志代码:
def test1():print('test1')logging.info("test1 is running")
但如果函数test2()、test3() 也有类似的需求呢?在每一个函数中去添加logging代码?这样就造成大量雷同的代码。装饰器的作用在这个时候就体现出来了。
三、具体应用
3.1 统计方法的耗时
#统计运行时间装饰器def run_time(function):@wraps(function)def wrapper():time_start=time.time()function()time_end=time.time()cost_time=time_end - time_startprint("花费时间:{}秒".format(cost_time))return wrapper #注意这里不要带(),否则会报错@run_timedef task():time.sleep(1)print("函数名称是:%s"% task.__name__)if __name__=='__main__':task()
输出结果:
3.2 日志打印装饰器+业务逻辑函数需要参数
#业务逻辑函数需要参数的情况def use_logging(function):@wraps(function)def wrapper(*args, **kwargs): # *args是一个数组,**kwargs是一个字典logging.warning("%s is running" %function.__name__)return function(*args,**kwargs)return wrapper@use_loggingdef foo(name, age=None, height=None):print("I am %s, age %s, height %s" %(name, age, height))
输出结果:
3.3 装饰器也可以带参数,提供更高的灵活性,比如不同业务函数可能需要的日志级别是不一样的
#装饰器带入参的情况def logging_level(level):def decorator(function):@wraps(function)# *args是一个数组,**kwargs是一个字典def wrapper(*args, **kwargs):if level == "warn":logging.warning("%s is running" %function.__name__)elif level == "info":logging.info("%s is running" %function.__name__)return function(*args,**kwargs)return wrapperreturn decorator@logging_level(level="info")def student(name, score=None):print("I am %s, score is %s" %(name, score))
输出结果:
3.4 整体练习使用代码:
"""该类主要用于练习装饰器使用"""from functools import wrapsimport loggingimport time#默认生成的root logger的level是logging.WARNING,低于该级别的就不输出了,因此这里设置一下日志输出级别为logging.INFOlogging.getLogger().setLevel(logging.INFO)#统计运行时间装饰器def run_time(function):@wraps(function) #@wraps也是一个装饰器,用于把原函数的元信息拷贝到装饰器里面的function函数。不加这个的话,原函数的信息会被wrapper取代def wrapper():time_start=time.time()function()time_end=time.time()cost_time=time_end - time_startprint("花费时间:{}秒".format(cost_time))return wrapper #注意这里不要带(),否则会报错#业务逻辑函数需要参数的情况def use_logging(function):@wraps(function)def wrapper(*args, **kwargs): # *args是一个数组,**kwargs是一个字典logging.warning("%s is running" %function.__name__)return function(*args,**kwargs)return wrapper#装饰器带入参的情况def logging_level(level):def decorator(function):@wraps(function)def wrapper(*args, **kwargs): # *args是一个数组,**kwargs是一个字典if level == "warn":logging.warning("%s is running" %function.__name__)elif level == "info":logging.info("%s is running" %function.__name__)return function(*args,**kwargs)return wrapperreturn decorator@run_timedef task():time.sleep(1)print("函数名称是:%s"% task.__name__)@use_loggingdef foo(name, age=None, height=None):print("I am %s, age %s, height %s" %(name, age, height))@logging_level(level="info")def student(name, score=None):print("I am %s, score is %s" %(name, score))if __name__=='__main__':task()foo("mytest",age="18", height="180cm")student("wahaha",score=90)```
四、python中常用的装饰器
@ddt:自动化测试数据驱动装饰器
@staticmethod:静态函数装饰器,使用后可以不需要实例化,直接类名.方法名()来调用
@property:标记类的方法装饰器,把一个方法变成属性调用,快速实现set和get方法
具体使用场景,后续文章中我再介绍~
END
以上就是本篇文章的全部内容,希望对你有帮助!
扫码关注程序员杨叔的微信公众号,免费获取更多全栈测试干货内容资料:
文章转载自程序员杨叔,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。




