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 相关文章推荐
天翼开放平台免费短信验证码接口使用实例
Dec 18 Python
Python爬虫框架Scrapy安装使用步骤
Apr 01 Python
python2.6.6如何升级到python2.7.14
Apr 08 Python
python list删除元素时要注意的坑点分享
Apr 18 Python
Django添加favicon.ico图标的示例代码
Aug 07 Python
win10 64bit下python NLTK安装教程
Sep 19 Python
Python实战购物车项目的实现参考
Feb 20 Python
Python Lambda函数使用总结详解
Dec 11 Python
Python enumerate内置库用法解析
Feb 24 Python
Python实现常见的几种加密算法(MD5,SHA-1,HMAC,DES/AES,RSA和ECC)
May 09 Python
python异常处理之try finally不报错的原因
May 18 Python
python 基于opencv实现图像增强
Dec 23 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
解决phpmyadmin中文乱码问题。。。
2007/01/18 PHP
PHP 7.1新特性的汇总介绍
2016/12/16 PHP
JavaScript 对象的属性和方法4种不同的类型
2010/03/19 Javascript
Jquery图形报表插件 jqplot简介及参数详解
2012/10/10 Javascript
使用jQuery.fn自定义jQuery翻页插件
2013/01/20 Javascript
DOM事件阶段以及事件捕获与事件冒泡先后执行顺序(图文详解)
2015/08/18 Javascript
JS实现图文并茂的tab选项卡效果示例【附demo源码下载】
2016/09/21 Javascript
vue组件之间通信实例总结(点赞功能)
2018/12/05 Javascript
微信小程序如何修改radio和checkbox的默认样式和图标
2019/07/24 Javascript
JavaScript switch语句使用方法简介
2019/12/30 Javascript
JavaScript实现公告栏上下滚动效果
2020/03/13 Javascript
原生JavaScript创建不可变对象的方法简单示例
2020/05/07 Javascript
Vue切换组件实现返回后不重置数据,保留历史设置操作
2020/07/21 Javascript
Js Snowflake(雪花算法)生成随机ID的实现方法
2020/08/26 Javascript
antd design table更改某行数据的样式操作
2020/10/31 Javascript
[01:00]一分钟回顾2018DOTA2亚洲邀请赛现场活动
2018/04/07 DOTA
[01:03:37]Secret vs VGJ.S Supermajor小组赛C组 BO3 第二场 6.3
2018/06/04 DOTA
Python手机号码归属地查询代码
2016/05/04 Python
在python3环境下的Django中使用MySQL数据库的实例
2017/08/29 Python
判断python对象是否可调用的三种方式及其区别详解
2019/01/31 Python
python selenium firefox使用详解
2019/02/26 Python
Django 过滤器汇总及自定义过滤器使用详解
2019/07/19 Python
使用Python实现图像标记点的坐标输出功能
2019/08/14 Python
Python使用贪婪算法解决问题
2019/10/22 Python
浅谈keras中Dropout在预测过程中是否仍要起作用
2020/07/09 Python
Python requests接口测试实现代码
2020/09/08 Python
详解淘宝H5 sign加密算法
2020/08/25 HTML / CSS
中专生学习生活的自我评价分享
2013/10/27 职场文书
捐书寄语赠言
2014/01/18 职场文书
科级干部考察材料
2014/02/15 职场文书
军训感想500字
2014/02/20 职场文书
图书室标语
2014/06/21 职场文书
小学趣味运动会加油稿
2014/09/25 职场文书
挂职个人工作总结
2015/03/05 职场文书
2015年会计工作总结范文
2015/05/26 职场文书
婚礼答谢词范文
2015/09/29 职场文书