python基础语法 | 装饰器
装饰器模式
装饰器模式(Decorator Pattern)允许向一个现有的对象添加新的功能,同时又不改变其结构。
这种模式创建了一个装饰类,用来包装原有的类,并在保持类方法签名完整性的前提下,提供了额外的功能。
实例 把一个形状装饰上不同的颜色,同时又不改变形状类。
若使用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
推荐阅读
-
字符串编码(charset,encoding/decoding)问题原理 博客分类: Web JVM浏览器数据结构RubyWindows
-
FastDFS初次安装体验 博客分类: 服务器运维
-
【转】centos7 df命令卡死解决方法 博客分类: 服务器运维
-
Nginx错误 博客分类: 服务器运维
-
ErLang / Python Web 的类似CGI/FastCGI模式 博客分类: Web ErlangPythonCGIWeb设计模式
-
CentOS7内外网双IP配置实践 博客分类: 服务器运维 CentOS7内外网双IP配置
-
阿里云磁盘扩容步骤 博客分类: 手记服务器运维
-
MySQL-python windows驱动下载 有64位版 博客分类: python MySQL-python
-
Zabbix安装日记 博客分类: 服务器运维
-
python 安装mysql-python模块 博客分类: python pythonmysql-python