今天写了一篇文章,详细讲解Python装饰器。文末有我的微信二维码。我打算建立一个读者群。想和Python交流的朋友可以加一下。装饰器是一种高级Python语法,是Python函数式编程的一部分。写法是@xxx,在pandas和django框架的源码中经常可以看到,被广泛使用。虽然没有装饰器不影响开发,但是使用装饰器可以让你的代码“秀”起来。当然,装饰器的作用不仅仅是“展示”,更重要的是可以让你的代码更加简洁。更高的可读性;业务逻辑解耦,更易维护。1函数基础在学习装饰器之前,我们先来了解一下Python函数的两个特点。可以在函数体中定义函数,也可以引用函数。有基础知识的朋友可以跳过,直接进入下一节。例1.在函数体中定义函数deffunc1():deffunc2():return"infunc2"res=func2()returnres在func1函数体中,定义并调用了一个func2函数,然后返回调用的结果。事实上,许多静态编程语言,如C和Java,都不支持在函数体中定义函数。但它在Python中是受支持的。例2.函数可以引用new_func=func1print(new_func())func1是一个函数,定义和例1一样,可以看到函数名可以像变量一样使用。赋值后new_func指向func1,可以直接通过new_func()调用该函数。\既然函数名可以像变量一样被赋值,那么自然可以通过return返回。deffunc1():deffunc2():return"infunc2"print(func2)returnfunc2new_func=func1()print(new_func)print(new_func())输出:.func2at490x100>2functionfunc1..func2at0x102499b80>infunc2print(func1)和print(new_func)具有相同的输出,这意味着它们是相同的函数。\2装饰器有了上面的基础,学习装饰器就很容易了。下面通过一个“炸土豆丝”的例子来学习装饰器。例3.“炸土豆丝”基础版deftu_dou_si():""""炸土豆丝"过程"""print('干净的土豆')print('去皮,切丝')print('炒')print('Outofthepot')这是最基本的版本。为了与国际接轨,例子中使用汉语拼音来定义函数名或变量名。让我们将示例3进行改造,将其变成装饰器版本。例4.“炸土豆丝”装饰器版本defmy_decorator(func):defwrapper():func()print('clearfried')print('outofthepot')returnwrapperdeftu_dou_si():print('washpotatoes')print('peeled,shredded')有没有发现和例子2很像,my_decorator函数是一个装饰器。它的参数func表示可以传入一个函数,里面定义了一个函数包装器,然后返回包装器函数。wrapper的中文意思是包裹和包裹,所以这里的意思是包裹了func(),包裹的效果就是多了两次打印输出。tu_dou_si函数只保留了土豆丝相关的打印语句。定义装饰器后,可以通过如下方式实现例3“炸土豆丝”的过程.对于上面的代码Python有一个更简洁的实现方式\@my_decoratordeftu_dou_si():print('cleanpotatoes')print('peeledandshredded')tu_dou_si()是一个Python装饰器。因为tu_dou_si函数已经被my_decorator修饰过,所以直接调用tu_dou_si函数就可以完成“炸土豆丝”的过程。3装饰器的优点虽然我们实现了装饰器,但是你心里可能会有疑惑。装饰器的写法并不简单,代码量比也不少。它的优点是什么?从“炸土豆丝”的单点来看,确实看不出装饰器的优势。如果先从线的角度来看,再从面的角度来看,就可以看出装饰器的优势了。“清炒土豆丝”流程可以拆分成两个业务,一个是准备配料的业务,即:print('干净的土豆')和print('去皮,切丝');另一个是制作食材的业务,即:print('fried')andprint('outofthepot')。之所以这样拆分,是因为这两部分是独立的子业务,很容易看出装饰器的优势。问题1.实现“炸山药”的另一个过程,怎么做@my_decoratordefshan_yao():print('cleanyam')print('去皮切丝')可以看到可以用装饰器实现复用在my_decorator中制作配料的业务流程,增加代码复用性,让整体代码更加简洁。问题2、想把“炸土豆丝”改成“凉拌土豆丝”,怎么办defmy_decorator(func):defwrapper():func()print('coldsalad')print('outofthepot')returnwrapper直接修改my_decorator业务即可,不会对其他业务产生副作用。理解了以上两个问题,我们也就知道装饰器的作用了。如果不使用装饰器(如:例3),要么产生代码冗余,要么业务耦合改变一个业务影响另一个业务。4装饰器的进阶下面我们来看看装饰器更高级的用法。例子5.装饰器的参数defcai_factory(cai_type='cooked'):defmy_decorator(func):defwrapper():func()print(f'{cai_type}')nrwtreappprint('出锅')returnmy_decorator@cai_factory('coldsalad')deftu_dou_si():print('cleanpotatoes')print('peeled,shredded')定义了一个cai_factory函数(即:蔬菜工厂),其中包含了之前定义的my_decorator装饰器.cai_factory函数接收cai_type参数来指定如何制作配料。定义tu_dou_si函数时,使用new装饰器,传入参数。例子6.装饰函数的参数defcai_factory(cai_type='cooked'):defmy_decorator(func):defwrapper(cai_name):func(cai_name)print(f'{cai_type}')print('出锅)returnwrapperreturnmy_decorator@cai_factory()defcai(cai_name='potato'):print(f'clean{cai_name}')print('peeled,shredded')定义一个由cai_factory修饰的函数cai,接收cai_name参数指定要制作的原料。同时在cai_factory装饰器中的wrapper函数中加入cai_name参数,调用func时传入该参数。示例7.修饰函数的不确定参数defcai_factory(cai_type='clearfried'):defmy_decorator(func):defwrapper(*args,**kwargs):func(*args,**kwargs)print(f'{cai_type}')print('出锅')returnwrapperreturnmy_decorator@cai_factory()defcai(cai_name,cai_cnt):print(f'clean{cai_name}{cai_cnt}')print('去皮,切丝')cai('yam','two')例6只能支持一个参数。本例将wrapper函数的参数改为args和*kwargs,可以支持任意参数,使装饰器更加通用。5装饰类类装饰器的用法与函数相同。这次我们停止做饭,开始玩游戏。游戏中皮肤与英雄的关系,完美展现了饰品与饰品的关系。下面我们定义一个英雄类,然后定义一个皮肤类来装饰它。defpi_fu(cls):类pifuclass:def__init__(self,name,pi_fu_name):self.wrapped=cls(name,pi_fu_name)self.pi_fu_name=pi_fu_namedefdef(self)'显示皮肤{self.pi_fu_name}')returnPiFuClass@pi_fuclassYingXiong:def__init__(self,name,pi_fu_name):self.name=nameself.pi_fu_name=pi_fu_namedefdisplay(self):显示英雄(selff'{.name}')ya_se=YingXiong('亚瑟','死亡骑士')ya_se.display()的写法和用法与函数装饰器类似,这里不再赘述。6内置装饰器Python有一些内置装饰器。这里介绍几个我们经常用到的:@property:修饰类方法,这样我们可以像访问属性一样获取函数的返回值@staticmethod:修饰类方法,表示该方法是一个静态方法,可以被直接调用而不实例化@wraps:函数进行装饰,这样我们就可以在装饰器中访问装饰前函数的属性。简单概括一下,Python装饰器支持装饰方法和Classes,可能会增加被装饰方法和方法的功能。同时也可以起到业务间解耦,增加代码复用性,简化代码的作用。以上就是本次分享的全部内容。觉得文章还不错的话,请关注公众号:Python编程学习圈,每日干货分享,发送“J”还能领取大量学习资料。或者去编程学习网了解更多编程技术知识。