今天给大家分享一些关于装饰器的知识点。理解。你好,装饰器装饰器本质上是一个Python函数,它允许其他函数在不更改任何代码的情况下添加额外的功能。装饰器的返回值也是一个函数对象。常用于有横切需求的场景,如:插入日志、性能测试、事务处理、缓存、权限验证等场景。装饰器是解决此类问题的绝佳设计。有了装饰器,我们就可以提取出很多与函数本身无关的雷同代码,继续复用。装饰器的使用方法非常固定。先定义一个装饰器(帽子),然后定义你的业务函数或类(人),最后把装饰器(帽子)附加到函数(人)上,如下#DefinitionDecoratordefdecorator(func):defwrapper(*args,**kw):returnfunc()returnwrapper#Definebusinessfunctionsanddecorate@decoratordeffunction():print("hello,decorator")其实,装饰器并不是编码所必需的,也就是说你不需要使用装饰器,它是好的。它的出现应该让我们的代码更加优雅,代码结构更加清晰。将具体的功能代码封装成装饰器,提高代码重用率,增强代码可读性下面我将举例说明如何编写各种简单和复杂的装饰器。第一种:普通装饰器首先我们来写一个最普通的装饰器。它的作用是:函数执行前先记录一行log,执行完后记录一行log#这是装饰函数,参数func是装饰函数deflogger(func):defwrapper(*args,**kw):print('我准备开始执行了:{}function:'.format(func.__name__))#真正的执行是这一行。func(*args,**kw)print('Master,I'mdone.')returnwrapper假设,我的业务功能是计算两个数的和。写完后,立即戴上帽子。@loggerdefadd(x,y):print('{}+{}={}'.format(x,y,x+y))然后执行add函数。Add(200,50)看看输出是什么?我要开始执行了:addfunction:200+50=250我执行完了。第二种:带参数的函数装饰器通过上面两个简单的入门例子,你应该能够理解装饰器的工作原理。然而,装饰器的使用远不止于此,还有很多文章需要研究。今天就让我们一起来学习这些知识吧。回头看上面的例子,装饰器是不能接收参数的。它的用法只能应用于一些简单的场景。不传递参数的装饰器只能在被装饰的函数上执行固定的逻辑。装饰器本身就是一个函数。作为一个函数,如果不能传递参数,那么这个函数的作用就会非常有限,只能执行固定的逻辑。这意味着如果装饰器的逻辑代码的执行需要根据不同的场景进行调整,如果不能传递参数,我们不得不写两个装饰器,这显然是不合理的。比如我们要实现一个定时发送邮件的任务(一分钟一封),一个定时定时(一天一次)进行时间同步的任务,我们可以实现一个periodic_task(定时任务)装饰器:我们自己。这个装饰器可以接收一个时间间隔参数,每隔多长时间执行一次任务。可以这样写。由于此功能代码比较复杂,不利于学习,这里就不贴出来了。@periodic_task(spacing=60)defsend_mail():pass@periodic_task(spacing=86400)defntp()pass然后我们自己来创建一个伪场景,我们可以在装饰器中传入一个参数来表示国籍,在之前功能被执行,用你国家的母语问好。#小明,中国@say_hello("china")defxiaoming():pass#jack,American@say_hello("america")defjack():pass那如果我们实现这个装饰器,让它实现传参呢?会比较复杂,需要两层嵌套。defsay_hello(contry):defwrapper(func):defdeco(*args,**kwargs):ifcontry=="china":print("Hello!")elifcontry=="america":print('hello.')else:return#真正执行函数的地方func(*args,**kwargs)returndecoreturnwrapper执行xiaoming()print("------------")jack()看输出结果.你好!------你好。第三种:无参类装饰器以上都是基于函数实现的装饰器。在阅读别人的代码时,经常可以发现还有一个基于类的装饰器。基于类装饰器的实现,必须实现两个内置函数__call__和__init__。__init__:接收修饰函数__call__:实现修饰逻辑。还是以日志打印的简单例子为例()isrunning..."\.format(func=self.func.__name__))returnself.func(*args,**kwargs)@loggerdefsay(something):print("say{}!".format(something)))执行say("hello")并查看输出[INFO]:函数say()正在运行...sayhello!第四种:带参数的类装饰器上面没有参数的例子,你发现不行,只能打印INFO级别的日志,一般情况下,我们还需要打印DEBUGWARNING等级别的日志。这需要将参数传递给类装饰器以指定此函数的级别。带参数和不带参数的类装饰器有很大的区别。__init__:接收传入参数,而不是接收装饰函数。__call__:接收装饰函数,实现装饰逻辑。classlogger(object):def__init__(self,level='INFO'):self.level=leveldef__call__(self,func):#acceptfunctiondefwrapper(*args,**kwargs):print([{level}]:函数{func}()正在运行..."\.format(level=self.level,func=func.__name__))func(*args,**kwargs)returnwrapper#returnfunction@logger(level='WARNING')defsay(something):print("say{}!".format(something))say("hello")我们指定WARNING级别,运行它以查看输出。[警告]:函数say()正在运行……打个招呼!第五:使用部分函数和类来实现装饰器大多数装饰器都是基于函数和闭包来实现的,但这并不是制作装饰器的唯一方式。事实上,Python对于一个对象是否可以使用装饰器(@decorator)的形式只有一个要求:装饰器必须是一个“可调用”的对象。对于这个可调用对象,我们最熟悉的就是函数。除了函数,类也可以是可调用对象,只要实现了__call__函数即可(上面的例子已经接触过)。还有一些容易被忽视的部分功能。它们实际上是可调用对象。接下来,让我们谈谈他们。如何使用类和部分函数的组合来实现独特的装饰器。如下图,DelayFunc是一个实现了__call__的类,delay返回的是偏函数,这里delay可以作为装饰器。(以下代码摘自PythonCraftsman:装饰器使用技巧)等待{self.duration}seconds...')time.sleep(self.duration)returnself.func(*args,**kwargs)defeager_call(self,*args,**kwargs):print('Callwithoutdelay')回归自我。func(*args,**kwargs)defdelay(duration):"""装饰器:延迟一个函数的执行,同时提供.eager_call方法立即执行"""#这里,为了避免定义额外的函数,#直接使用functools.partial有助于构造一个DelayFunc实例returnfunctools.partial(DelayFunc,duration)我们的业务函数很简单,只需要加上@delay(duration=2)defadd(a,b):returna+b即可看执行过程>>>add#可以看出add变成了Delay的实例<__main__.DelayFuncobjectat0x107bd0be0>>>>>>>>add(3,5)#直接调用实例进入__call__Waitfor2seconds...8>>>>>>add.func#实现示例方法
