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字符编码与函数的基本使用方法
Sep 30 Python
python中如何正确使用正则表达式的详细模式(Verbose mode expression)
Nov 08 Python
Django后台获取前端post上传的文件方法
May 28 Python
Python使用cx_Freeze库生成msi格式安装文件的方法
Jul 10 Python
pygame游戏之旅 添加游戏界面按键图形
Nov 20 Python
解决在pycharm中显示额外的 figure 窗口问题
Jan 15 Python
windows系统中Python多版本与jupyter notebook使用虚拟环境的过程
May 15 Python
一篇文章弄懂Python中所有数组数据类型
Jun 23 Python
python字典嵌套字典的情况下找到某个key的value详解
Jul 10 Python
Python with语句和过程抽取思想
Dec 23 Python
Python叠加矩形框图层2种方法及效果
Jun 18 Python
Selenium Webdriver元素定位的八种常用方式(小结)
Jan 13 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文件生成的图片无法使用CDN缓存的解决方法
2015/06/20 PHP
php的laravel框架快速集成微信登录的方法
2016/12/12 PHP
php根据地址获取百度地图经纬度的实例方法
2019/09/03 PHP
解析arp病毒背后利用的Javascript技术附解密方法
2007/08/06 Javascript
40款非常棒的jQuery 插件和制作教程(系列二)
2011/11/02 Javascript
动态创建script在IE中缓存js文件时导致编码的解决方法
2014/05/04 Javascript
jQuery判断元素是否存在的可靠方法
2014/05/06 Javascript
vue自定义全局组件(自定义插件)的用法
2018/01/30 Javascript
vuejs实现ready函数加载完之后执行某个函数的方法
2018/08/31 Javascript
vue拖拽排序插件vuedraggable使用方法详解
2020/08/21 Javascript
JS实现简单日历特效
2020/01/03 Javascript
vue npm install 安装某个指定的版本操作
2020/08/11 Javascript
[03:46]DAC趣味视频-中文考试.mp4
2017/04/02 DOTA
Python简单计算文件夹大小的方法
2015/07/14 Python
python实现中文转换url编码的方法
2016/06/14 Python
Python简单实现安全开关文件的两种方式
2016/09/19 Python
Python字符串处理实现单词反转
2017/06/14 Python
使用tensorflow实现AlexNet
2017/11/20 Python
tensorflow学习笔记之简单的神经网络训练和测试
2018/04/15 Python
python实现自动登录
2018/09/17 Python
对Python协程之异步同步的区别详解
2019/02/19 Python
python3 自动识别usb连接状态,即对usb重连的判断方法
2019/07/03 Python
处理python中多线程与多进程中的数据共享问题
2019/07/28 Python
python3.4中清屏的处理方法
2020/07/06 Python
8款使用 CSS3 实现超炫的 Loading(加载)的动画效果
2015/03/17 HTML / CSS
使用phonegap创建联系人的实现方法
2017/03/30 HTML / CSS
泰国排名第一的家居用品中心:HomePro
2020/11/18 全球购物
廉政教育心得体会
2014/01/01 职场文书
小班评语大全
2014/05/04 职场文书
2015年世界无车日活动总结
2015/03/23 职场文书
行政主管岗位职责范本
2015/04/09 职场文书
毕业生政审意见范文
2015/06/04 职场文书
Python中异常处理用法
2021/11/27 Python
Python数据处理的三个实用技巧分享
2022/04/01 Python
Win11 引入 Windows 365 云操作系统,适应疫情期间混合办公模式:启动时直接登录、模
2022/04/06 数码科技
Windows11 Insider Preview Build 25206今日发布 更新内容汇总
2022/09/23 数码科技