欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

python基础语法 | 装饰器

程序员文章站 2024-03-18 22:24:52
...

装饰器模式

装饰器模式(Decorator Pattern)允许向一个现有的对象添加新的功能,同时又不改变其结构。
这种模式创建了一个装饰类,用来包装原有的类,并在保持类方法签名完整性的前提下,提供了额外的功能

实例 把一个形状装饰上不同的颜色,同时又不改变形状类。

python基础语法 | 装饰器
若使用Java实现,则类图如上图所示,这里不进行过多的介绍,本章主要介绍python中的装饰器。

函数

在介绍python的装饰器之前,先了解一下python的函数,与Java不同的是,在python中一切皆对象,函数也是对象,可以进行赋值,可以作为参数,函数中还可以定义函数,也可以返回函数,看下四个例子:

例1:赋值

>>> # 定义了一个函数
>>> def rectangle():
... 	print('draw a rectangle')
... 
>>> rectangle()
draw a rectangle

>>> # 将指向函数的变量rectangle赋值给变量draw
>>> draw=rectangle
>>> #此时,变量draw也指向方法所在的地址
>>> draw
<function rectangle at 0x0000021406108D30>
>>> draw()
draw a rectangle

>>> # 函数赋值时,rectangle后不能加括号,rectangle()代表执行函数,然后将返回值赋值给变量draw
>>> draw=rectangle()
draw a rectangle
>>> type(draw)
<class 'NoneType'>

>>> # 删除变量rectangle并不会删除内存中的方法
>>> draw=rectangle
>>> del rectangle
>>> draw
<function rectangle at 0x0000021406108D30>
>>> rectangle()
Traceback (most recent call last):
  File "<interactive input>", line 1, in <module>
NameError: name 'rectangle' is not defined
>>> draw()
draw a rectangle

例2:将函数作为参数传递

>>> # 定义了一个函数
>>> def rectangle():
... 	print('draw a rectangle')
... 
>>> # 定义一个函数,该函数的参数是另外一个函数
>>> def draw(shape):
... 	shape()
... 
>>> # 将指向函数的变量rectangle作为参数传递给函数draw
>>> draw(rectangle)
draw a rectangle

例3:函数中定义函数

>>> # 在函数draw中定义函数printer,然后进行调用
>>> def draw():
... 	def printer():
... 		print('使用蓝色的画笔')
... 	printer()
... 
>>> # 调用draw()函数,draw()函数调用了printer()函数
>>> draw()
使用蓝色的画笔
>>> # printer()函数只能在draw()函数中使用
>>> printer()
Traceback (most recent call last):
  File "<interactive input>", line 1, in <module>
NameError: name 'printer' is not defined

例4:函数中返回函数

>>> # 在函数draw中定义函数printer(),然后返回指向函数的变量printer	
>>> def draw():
... 	def printer():
... 		print('使用蓝色的画笔')
... 	return printer
... 
>>> # draw()返回的是变量printer,后面的括号用来执行函数printer()
>>> draw()()
使用蓝色的画笔

实例 使用蓝色的画笔画一个矩形

结合上面的四个例子,实现对rectangle函数进行处理

>>> def rectangle():
... 	print('draw a rectangle')
... 
>>> def draw(shape):
... 	def printer():
... 		print('使用蓝色的画笔')
... 		shape()
... 	return printer
... 
>>> rectangle=draw(rectangle)
>>> rectangle()
使用蓝色的画笔
draw a rectangle

这里的draw()方法就是一个装饰器,给原本的“矩形”装饰了“蓝色”,

装饰器函数

基于上面的实例,通过下面的代码看出,rectangle被装饰后的函数签名也被修改了,python中提供了解决这个问题的方法,那就是functools.wraps

>>> print(rectangle.__name__)
printer

>>> def rectangle():
... 	print('draw a rectangle')
... 
>>> # 增加wraps装饰器
>>> def draw(shape):
... 	@wraps(shape)
... 	def printer():
... 		print('使用蓝色的画笔')
... 		shape()
... 	return printer
... 
>>> rectangle=draw(rectangle)
>>> rectangle()
使用蓝色的画笔
draw a rectangle
>>> print(rectangle.__name__)
rectangle

从上面的例子看出wraps就是我们平时看到的python中的装饰器,它接受一个函数来进行装饰,并加入了复制函数名称、注释文档、参数列表等等的功能。这可以让我们在装饰器里面访问在装饰之前的函数的属性。
’@’ 符号其实时python提供的一个装饰器的语法糖,

>>> @draw
... def rectangle():
... 	print('draw a rectangle')
... 
>>> # 等价于下面的表达式
>>> rectangle=draw(rectangle)

因此,上面的例子可以修改如下,就是在python中看到的装饰器

>>> def draw(shape):
... 	@wraps(shape)
... 	def printer():
... 		print('使用蓝色的画笔')
... 		shape()
... 	return printer
... 
>>> @draw
... def rectangle():
... 	print('draw a rectangle')
... 
>>> rectangle()
使用蓝色的画笔
draw a rectangle

带参数的装饰器函数

跟functools提供的@wraps(shape)一样,装饰器怎么带参数,
例如,
1、将矩形画在指定的文件中,
2、自定义矩形的长宽高

>>> def draw_file(file_name):
... 	def draw(shape):
... 		@wraps(shape)
... 		def printer(*args,**kargs):
... 			print('在文件[%s]中绘制'%(file_name,))
... 			print('使用蓝色的画笔')
... 			shape(*args,**kargs)
... 		return printer
... 	return draw
... 
>>> @draw_file('a.txt')
... def rectangle(x,y,z):
... 	print('draw a size[%d] rectangle'%(x*y*z,))
... 
>>> rectangle(1,2,3)
在文件[a.txt]中绘制
使用蓝色的画笔
draw a size[6] rectangle

上面的例子可以看出,这里主要是利用在函数中嵌套装饰器函数的方法来处理装饰器的参数,然后通过*args和**kargs变量处理被装饰函数的参数,*args表示有一个或多个单值参数,**kargs表示有一个或多个键值对参数

装饰器类

从上面的例子可以看出,在函数中嵌套装饰器函数看起来不是很简洁,使用类来构建装饰器更加简洁

>>> class draw(object):
... 	def __init__(self,file_name):
... 		self.file_name=file_name
... 	def __call__(self,shape):
... 		@wraps(shape)
... 		def printer(*args,**kargs):
... 			print('在文件[%s]中绘制'%(self.file_name,))
... 			print('使用蓝色的画笔')
... 			shape(*args,**kargs)
... 		return printer
... 
>>> # @draw('a.txt')将调用draw的构造函数,然后调用类的__call__方法
>>> @draw('a.txt')
... def rectangle(x,y,z):
... 	print('draw a size[%d] rectangle'%(x*y*z,))
... 
>>> rectangle(1,2,3)
在文件[a.txt]中绘制
使用蓝色的画笔
draw a size[6] rectangle
相关标签: # python开发