Python深入学习之装饰器


Posted in Python onAugust 31, 2014

装饰器(decorator)是一种高级Python语法。装饰器可以对一个函数、方法或者类进行加工。在Python中,我们有多种方法对函数和类进行加工,比如在Python闭包中,我们见到函数对象作为某一个函数的返回结果。相对于其它方式,装饰器语法简单,代码可读性高。因此,装饰器在Python项目中有广泛的应用。

装饰器最早在Python 2.5中出现,它最初被用于加工函数和方法这样的可调用对象(callable object,这样的对象定义有__call__方法)。在Python 2.6以及之后的Python版本中,装饰器被进一步用于加工类。

装饰函数和方法

我们先定义两个简单的数学函数,一个用来计算平方和,一个用来计算平方差:

# get square sum

def square_sum(a, b):

    return a**2 + b**2
# get square diff

def square_diff(a, b):

    return a**2 - b**2
print(square_sum(3, 4))

print(square_diff(3, 4))

在拥有了基本的数学功能之后,我们可能想为函数增加其它的功能,比如打印输入。我们可以改写函数来实现这一点:

# modify: print input
# get square sum

def square_sum(a, b):

    print("intput:", a, b)

    return a**2 + b**2
# get square diff

def square_diff(a, b):

    print("input", a, b)

    return a**2 - b**2
print(square_sum(3, 4))

print(square_diff(3, 4))

我们修改了函数的定义,为函数增加了功能。

现在,我们使用装饰器来实现上述修改:

def decorator(F):

    def new_F(a, b):

        print("input", a, b)

        return F(a, b)

    return new_F
# get square sum

@decorator

def square_sum(a, b):

    return a**2 + b**2
# get square diff

@decorator

def square_diff(a, b):

    return a**2 - b**2
print(square_sum(3, 4))

print(square_diff(3, 4))

装饰器可以用def的形式定义,如上面代码中的decorator。装饰器接收一个可调用对象作为输入参数,并返回一个新的可调用对象。装饰器新建了一个可调用对象,也就是上面的new_F。new_F中,我们增加了打印的功能,并通过调用F(a, b)来实现原有函数的功能。

定义好装饰器后,我们就可以通过@语法使用了。在函数square_sum和square_diff定义之前调用@decorator,我们实际上将square_sum或square_diff传递给decorator,并将decorator返回的新的可调用对象赋给原来的函数名(square_sum或square_diff)。 所以,当我们调用square_sum(3, 4)的时候,就相当于:

square_sum = decorator(square_sum)

square_sum(3, 4)

我们知道,Python中的变量名和对象是分离的。变量名可以指向任意一个对象。从本质上,装饰器起到的就是这样一个重新指向变量名的作用(name binding),让同一个变量名指向一个新返回的可调用对象,从而达到修改可调用对象的目的。

与加工函数类似,我们可以使用装饰器加工类的方法。

如果我们有其他的类似函数,我们可以继续调用decorator来修饰函数,而不用重复修改函数或者增加新的封装。这样,我们就提高了程序的可重复利用性,并增加了程序的可读性。

含参的装饰器

在上面的装饰器调用中,比如@decorator,该装饰器默认它后面的函数是唯一的参数。装饰器的语法允许我们调用decorator时,提供其它参数,比如@decorator(a)。这样,就为装饰器的编写和使用提供了更大的灵活性。

# a new wrapper layer

def pre_str(pre=''):

    # old decorator

    def decorator(F):

        def new_F(a, b):

            print(pre + "input", a, b)

            return F(a, b)

        return new_F

    return decorator
# get square sum

@pre_str('^_^')

def square_sum(a, b):

    return a**2 + b**2
# get square diff

@pre_str('T_T')

def square_diff(a, b):

    return a**2 - b**2
print(square_sum(3, 4))

print(square_diff(3, 4))

上面的pre_str是允许参数的装饰器。它实际上是对原有装饰器的一个函数封装,并返回一个装饰器。我们可以将它理解为一个含有环境参量的闭包。当我们使用@pre_str('^_^')调用的时候,Python能够发现这一层的封装,并把参数传递到装饰器的环境中。该调用相当于:
square_sum = pre_str('^_^') (square_sum)

装饰类

在上面的例子中,装饰器接收一个函数,并返回一个函数,从而起到加工函数的效果。在Python 2.6以后,装饰器被拓展到类。一个装饰器可以接收一个类,并返回一个类,从而起到加工类的效果。

def decorator(aClass):

    class newClass:

        def __init__(self, age):

            self.total_display   = 0

            self.wrapped         = aClass(age)

        def display(self):

            self.total_display += 1

            print("total display", self.total_display)

            self.wrapped.display()

    return newClass
@decorator

class Bird:

    def __init__(self, age):

        self.age = age

    def display(self):

        print("My age is",self.age)
eagleLord = Bird(5)

for i in range(3):

    eagleLord.display()

在decorator中,我们返回了一个新类newClass。在新类中,我们记录了原来类生成的对象(self.wrapped),并附加了新的属性total_display,用于记录调用display的次数。我们也同时更改了display方法。

通过修改,我们的Bird类可以显示调用display的次数了。

总结

装饰器的核心作用是name binding。这种语法是Python多编程范式的又一个体现。大部分Python用户都不怎么需要定义装饰器,但有可能会使用装饰器。鉴于装饰器在Python项目中的广泛使用,了解这一语法是非常有益的。

Python 相关文章推荐
简单的Python的curses库使用教程
Apr 11 Python
python关闭windows进程的方法
Apr 18 Python
Python的Django框架中自定义模版标签的示例
Jul 20 Python
Python中MySQLdb和torndb模块对MySQL的断连问题处理
Nov 09 Python
Python实现将一个大文件按段落分隔为多个小文件的简单操作方法
Apr 17 Python
解决pandas中读取中文名称的csv文件报错的问题
Jul 04 Python
NLTK 3.2.4 环境搭建教程
Sep 19 Python
利用Python半自动化生成Nessus报告的方法
Mar 19 Python
使用Python爬取弹出窗口信息的实例
Mar 14 Python
python如何实现图片压缩
Sep 11 Python
pandas抽取行列数据的几种方法
Dec 13 Python
如何使用Python进行PDF图片识别OCR
Jan 22 Python
Python深入学习之闭包
Aug 31 #Python
Python深入学习之对象的属性
Aug 31 #Python
Python深入学习之上下文管理器
Aug 31 #Python
Python深入学习之特殊方法与多范式
Aug 31 #Python
python中的reduce内建函数使用方法指南
Aug 31 #Python
Python中使用ConfigParser解析ini配置文件实例
Aug 30 #Python
python进阶教程之动态类型详解
Aug 30 #Python
You might like
PHP字符串的编码问题的详细介绍
2013/04/27 PHP
浅谈discuz密码加密的方式
2014/05/22 PHP
Zend Framework教程之Application和Bootstrap用法详解
2016/03/10 PHP
Yii2实现增删改查后留在当前页的方法详解
2017/01/13 PHP
详谈php中 strtr 和 str_replace 的效率问题
2017/05/14 PHP
PHP实现创建微信自定义菜单的方法示例
2017/07/14 PHP
php数据库的增删改查 php与javascript之间的交互
2017/08/31 PHP
PHP getName()函数讲解
2019/02/03 PHP
js下获取div中的数据的原理分析
2010/04/07 Javascript
JavaScript判断窗口是否最小化的代码(跨浏览器)
2010/08/01 Javascript
Angular下H5上传图片的方法(可多张上传)
2017/01/09 Javascript
利用imgareaselect辅助后台实现图片上传裁剪
2017/03/02 Javascript
JavaScript你不知道的一些数组方法
2017/08/18 Javascript
浅谈webpack devtool里的7种SourceMap模式
2019/01/14 Javascript
使用uni-app开发微信小程序的实现
2019/12/13 Javascript
javascript 模块依赖管理的本质深入详解
2020/04/30 Javascript
原生js实现轮播图特效
2020/05/04 Javascript
详解Vue+elementUI build打包部署后字体图标丢失问题
2020/07/13 Javascript
[47:53]DOTA2上海特级锦标赛主赛事日 - 1 败者组第一轮#2COL VS Spirit
2016/03/02 DOTA
[01:27:30]LGD vs Newbee 2019国际邀请赛小组赛 BO2 第二场 8.16
2019/08/19 DOTA
Python中str.format()详解
2017/03/12 Python
python 调用c语言函数的方法
2017/09/29 Python
详解python变量与数据类型
2020/08/25 Python
python爬虫看看虎牙女主播中谁最“顶”步骤详解
2020/12/01 Python
Python爬虫定时计划任务的几种常见方法(推荐)
2021/01/15 Python
在html5的Canvas上绘制椭圆的几种方法总结
2013/01/07 HTML / CSS
HTML5、Select下拉框右边加图标的实现代码(增进用户体验)
2017/10/16 HTML / CSS
美国马匹用品和骑马配件购物网站:Horse.com
2018/01/08 全球购物
顶丰TOPPIK台湾官网:增发纤维假发,告别秃发困扰
2018/06/13 全球购物
Lulu Guinness露露·吉尼斯官网:红唇包
2019/02/03 全球购物
装潢设计专业推荐信模板
2013/11/26 职场文书
八一建军节部队活动方案
2014/02/04 职场文书
学习雷锋精神心得体会范文
2014/03/12 职场文书
大学生暑假实习总结
2015/07/13 职场文书
预备党员入党感想
2015/08/10 职场文书
python人工智能human learn绘图可创建机器学习模型
2021/11/23 Python