当前位置: 首页 > 后端技术 > Python

装饰器需要使用

时间:2023-03-26 16:25:49 Python

装饰器这种方式在python中是一个非常重要的知识,但是对于大多数新手来说,装饰器有点难以理解,所以看到装饰器就想绕道而行,但是这样会留下一个给自己带来很多麻烦。接下来的一大隐患就是,一旦面试官让你实现一个简单的装饰器,你就会因为这个坑而错失良机。那么我们为什么要使用装饰器呢?因为写程序讲究一个OCP原则,即开闭原则,拒绝修改已经完成功能的代码,推荐扩展代码功能,所以这也意味着我们不能再修改已经完成的功能已经完成了,但是要去扩展他的功能,所以,我们需要用一个装饰器来做这个扩展的功能。我们将讨论创建通用装饰器、创建通用装饰器、使用多个装饰器进行装饰、带参数的装饰器以及装饰递归函数。1、普通装饰器的创建首先,装饰器的创建需要满足什么条件?装饰器本质上是一个闭包,所以也符合闭包的三个特点:函数嵌套内层函数需要使用外层函数的变量或参数外层函数返回内层函数的函数对象importtimedeffun_out(num):deffun_inner():begin=time.time()print(num)end=time.time()print('程序执行时间是%s'%(end-begin))returnfun_inner现在这个高级函数是一个闭包功能。那么既然使用了装饰器,当然我们还需要一个需要装饰的目标函数。deffun():foriinrange(200000):print(i)既然准备工具已经完成,那么我们如何让上面的闭包函数成为计算程序运行时间的装饰器呢?外层函数的参数必须也只能接受目标函数需要修饰的函数对象。在内层函数中,有外层函数参数的函数调用。importtimedeffun_out(fun):deffun_inner():begin=time.time()res=fun()end=time.time()print('程序执行时间是%s'%(end-begin))returnfun_inner现在我们的函数变成了装饰器函数。让我们调用程序看看结果。importtimedeffun_out(fun):deffun_inner():begin=time.time()fun()end=time.time()print('程序执行时间是%s'%(end-begin))unreturnfun(innerdeffun):foriinrange(200000):print(i)f=fun_out(fun)f()执行结果:0...199999程序执行时间为0.6352810859680176那么这就是我们的执行结果,可以看到我们都完成了添加了程序,但是没有改变程序本身的功能。但是这样写不是很友好。如果用户使用这个程序,步骤的增加会降低体验。同时这也会将开发者的注意力吸引到如何调用装饰器而不是装饰目标函数,所以我们一般使用语法糖。importtimedeffun_out(fun):deffun_inner():begin=time.time()fun()end=time.time()print('程序执行时间是%s'%(end-begin))returnfun_innerf@fun_outfun():foriinrange(200000):print(i)fun()执行结果:0...199999程序执行时间为0.6352810859680176这样使用比较方便。2.通用装饰器的创建通用装饰器,即被装饰的函数是否可以带参数或不带参数使用,就是通用装饰器。importtimedeffun_out(fun):deffun_inner(*args,**kwargs):begin=time.time()res=fun(*args,**kwargs)end=time.time()print('程序执行时间为%s'%(end-begin))returnresreturnfun_inner@fun_outdeffun1(a,b):time.sleep(1)returna+br=fun1(1,2)print(r)执行结果:程序执行时间对于1.00047683715820313我们在内部函数中加入变长参数,无论目标函数是否带参都可以直接使用这个装饰器。3、多个装饰器的使用大家都知道,使用一个装饰器,我们的目标函数是没有问题的,那如果使用多个装饰器呢?importtimedeffun_out(fun):deffun_inner(*args,**kwargs):begin=time.time()res=fun(*args,**kwargs)end=time.time()print('程序执行时间为%s'%(end-begin))returnresreturnfun_innerdeflogin(fun):defwrapper(*args,**kwargs):print('请先登录')res=urfun(*args,re**kwargs)resreturnwrapper@fun_out@logindeffun1(a,b):time.sleep(1)returna+br=fun1(1,2)print(r)运行结果:请先登录。程序的执行时间是1.00008726119995123那你知道吗注意程序的执行顺序。装饰器的执行顺序是从下往上执行的,而不是我们想象的从上往下执行。这是多个装饰器同时装饰一个目标函数的特殊属性。哪个装饰器如果装饰器靠近目标函数,则该装饰器将首先执行。4.带参数装饰器的使用带参数装饰器是一种特殊的装饰器。当我们使用带参数的装饰器时,我们必须在装饰器的外层包裹一层函数。能。deflogging(sign):deffun_out(fun):deffun_inner(num1,num2):inprogressifsign=="+":print("Callingaddition")elifsign=="-":Subtraction")res=fun(num1,num2)返回res返回fun_inner返回fun_out@logging("+")defadd(a,b):result=sub+b@returningfresult("a,b):result=a-breturnresultres=add(1,2)print(res)res=sub(1,2)print(res)其实也是一样的,由于装饰器的外层函数只能接受目标函数对象的参数,那么我们需要使用另一个函数来接受这个参数,所以我们可以直接用一层函数包裹装饰器。当然,我们必须在结束对象处返回装饰器的外部函数。5.装饰器装饰递归函数递归函数的装饰是一种特殊的现象。装饰器有一个机制。当它发现这个函数已经被装饰了,它就不会去装饰这个函数,而是直接停止。之所以对程序进行装饰,还是因为装饰器的内存原理。装饰函数的装饰器会把目标函数的对象保存到打开的内存空间中,所以当被装饰的目标函数的对象已经存在于内存空间中时,程序会直接报错。所以我们不能直接用装饰器来装饰递归函数,而是先用一个外层函数把递归函数包裹起来,让闭包函数在这个函数里跑完,再跑完。这时候装饰器就不会存在重复的函数对象了。importtimedeffun_out(fun):deffun_inner(*args,**kwargs):begin=time.time()res=fun(*args,**kwargs)end=time.time()print('程序执行时间为%s'%(end-begin))returnresreturnfun_inner@fun_out#求n的m次方defdecorator(n,m):deffun(n,m):time.sleep(1)ifm==1:returnnreturnn*fun(n,m-1)returnfun(n,m)res=decorator(10,5)print(res)执行结果:程序执行时间为5.0015575885772705100000这样,我们就可以完成Decorators对于递归函数。你没用吗?