通常,给一个对象添加新功能有3种方式:直接给对象所属的类添加方法,使用组合(在新类中创建原类的对象,reusetheexisting有类的功能)使用继承(可以使用已有的类,不需要重复写原来的类进行功能扩展)一般来说组合优先于继承,但是装饰器属于第四种,可以动态改变对象从而扩展对象的功能。一般装饰器的应用场景包括打印日志、性能测试、事务处理、权限验证等。Python的内置装饰器是如何工作的要了解Python装饰器的工作原理,您首先需要了解闭包的概念。闭包是指一个函数嵌套一个函数,内层嵌套函数调用外层函数的变量,外层函数返回内层函数,这样的结构就是闭包。装饰器是闭包的应用,但装饰器参数是作为函数传递的。简单闭包示例:defadd_num(x):defsum_num(y):returnx+yreturnsum_numadd_num5=add_num(5)total_num=add_num5(100)print(total_num)注意:add_num外层函数接受参数x并返回内层函数sum_num,而内层函数sum_num接受参数y,会与add_num外层函数接受的参数x相加,并返回结果。add_num5对象定义add_num外部函数接受的参数x为5,add_num5(100)返回的结果为105。装饰器的基本使用简单计数函数运行时装饰器示例:deftimes_use(func):defcount_times(*args,**kwargs):start=time.time()result=func(*args,**kwargs)end=time.time()print(end-start)returnresultreturncount_times@times_usedeftest_decorator():time.sleep(2)print("TestDecorator")test_decorator()注意:这里函数test_decorator是作为参数传入的times_use函数,内部函数count_times会保留原test_decorator函数的代码逻辑。在执行test_decorator之前先把执行前的时间保存起来,然后和执行后的时间进行比较,得到对应的耗时。使用装饰器的好处是在保留原有功能的基础上,可以在不修改原有功能或增加新封装的情况下,通过修改功能来增加新的功能。根据日志级别打印日志装饰器示例(带参数的装饰器):defuse_logging(level):defdecorator(func):defwrapper(*args,**kwargs):iflevel=="warn":logging.warn("%s正在运行"%func.__name__)result=func(*args,**kwargs)print(result)returnresultreturnwrapperreturndecorator@use_logging("warn")deftest_decorator():print("TestDecorator")return"Success"test_decorator()计算函数运行时间的类装饰器示例:classlogTime:def__init__(self,use_log=False):self._use_log=use_logdef__call__(self,func):def_log(*args,**kwargs):start_time=time.time()result=func(*args,**kwargs)print(result)end_time=time.time()ifself._use_log:print(end_time-start_time)返回结果返回_log@logTime(True)deftest_decorator():时间。睡觉(2)print("TestDecorator")return"Success"functoolswraps使用场景使用装饰器可以在保留原有代码逻辑的同时对函数进行扩展,但是会丢失原有函数中的元信息,比如__name__、__doc__、参数列表即可在这种情况下使用functools.wraps。wraps也是一个装饰器,但是它会将原函数的元信息复制到装饰器函数中。具体用法:fromfunctoolsimportwrapsdefuse_logging(level):defdecorator(func):@wraps(func)defwrapper(*args,**kwargs):iflevel=="warn":logging.warn("%sisrunning"%func.__name__)result=func(*args,**kwargs)print(result)返回结果returnwrapperreturndecorator@use_logging("warn")deftest_decorator():""""TestDecoratorDocString""""time.sleep(2)print("TestDecorator")return"Success"print(test_decorator.__name__)print(test_decorator.__doc__)注:wraps装饰器将传入的test_decorator函数中的元信息复制到wrapper中decorator函数,wrapper有test_decorator的meta信息。关于装饰器的执行顺序,日常业务中经常会用到多个装饰器,比如权限验证、登录验证、日志记录、性能测试等使用场景。因此,在使用多个装饰器时,会涉及到装饰器的执行顺序。先说结论吧。关于装饰器的执行顺序,可以分为两个阶段:(被装饰函数)定义阶段,(被装饰函数)执行阶段:函数定义阶段,执行顺序从距离函数最近的装饰器开始,从Inside-out执行;在函数执行阶段,执行顺序是从外到内,逐层执行。多装饰器示例:defdecorator_a(func):print("GetinDecorator_a")definner_a(*args,**kwargs):print("GetinInner_a")result=func(*args,**kwargs)returnresultreturninner_defdecorator_b(func):print("GetinDecorator_b")definner_b(*args,**kwargs):print("GetinInner_b")result=func(*args,**kwargs)返回结果returninner_b@decorator_b@decorator_adeftest_decorator():"""testdecoratorDocString"""print("TestDecorator")return"Success"运行结果:GetinDecorator_aGetinDecorator_bGetinInner_bGetinInner_aTestDecorator代码注释:以上函数可以配合使用decorator相当于decorator_b(decorator_a(test_decorator()),即test_dcorator函数作为参数传递给decorator_a函数,然后打印“GetinDecorator_a”,将inner_a函数返回给上层decorator_b函数,decorator_b函数接受inner_a函数作为参数,打印“GetinDecorator_b”,然后返回inner_b函数。此时test_decorator()调用inner_b函数,inner_b函数打印“Getininner_b”,然后调用inner_a函数,inner_a打印“GetinDecorator_a”,最后调用test_decorator函数。这样从最外层看,好像是直接调用了test_decorator函数,但是在刚才的流程中可以实现功能扩展。更多关于Python技术的干货内容,欢迎前往公众号【Python编程学习圈】了解。关注的话,可以获得大量的学习资料和教程,涵盖Python电子书、教程、数据库编程、Django、爬虫、云计算等。
