Python编程中装饰器的使用示例解析


Posted in Python onJune 20, 2016

装饰函数和方法

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

# 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的次数了。

Python 相关文章推荐
Python中文竖排显示的方法
Jul 28 Python
通过实例浅析Python对比C语言的编程思想差异
Aug 30 Python
使用PyV8在Python爬虫中执行js代码
Feb 16 Python
Python读取MRI并显示为灰度图像实例代码
Jan 03 Python
pandas中去除指定字符的实例
May 18 Python
Django框架基础模板标签与filter使用方法详解
Jul 23 Python
python selenium实现发送带附件的邮件代码实例
Dec 10 Python
python requests包的request()函数中的参数-params和data的区别介绍
May 05 Python
PyCharm中如何直接使用Anaconda已安装的库
May 28 Python
Python3合并两个有序数组代码实例
Aug 11 Python
举例讲解Python装饰器
Dec 24 Python
python中threading和queue库实现多线程编程
Feb 06 Python
12步入门Python中的decorator装饰器使用方法
Jun 20 #Python
深入学习Python中的装饰器使用
Jun 20 #Python
Python中Iterator迭代器的使用杂谈
Jun 20 #Python
实例讲解Python编程中@property装饰器的用法
Jun 20 #Python
Python的包管理器pip更换软件源的方法详解
Jun 20 #Python
python3.5使用tkinter制作记事本
Jun 20 #Python
浅谈python抛出异常、自定义异常, 传递异常
Jun 20 #Python
You might like
Protoss建筑一览
2020/03/14 星际争霸
关于UEditor编辑器远程图片上传失败的解决办法
2012/08/31 PHP
基于php无限分类的深入理解
2013/06/02 PHP
thinkphp自定义权限管理之名称判断方法
2017/04/01 PHP
php实现微信企业号支付个人的方法详解
2017/07/26 PHP
php实现小程序支付完整版
2018/10/09 PHP
将list转换为json失败的原因
2013/12/17 Javascript
两个select多选模式的选项相互移动(示例代码)
2014/01/11 Javascript
使用原生JS实现弹出层特效
2014/12/22 Javascript
js简单实现标签云效果实例
2015/08/06 Javascript
基于JavaScript创建动态Dom
2015/12/08 Javascript
Node.js下自定义错误类型详解
2016/10/17 Javascript
在javaScript中检测数据类型的几种方式小结
2017/03/04 Javascript
微信小程序 页面跳转事件绑定的实例详解
2017/09/20 Javascript
vue jsx 使用指南及vue.js 使用jsx语法的方法
2017/11/11 Javascript
Mint-UI时间组件起始时间问题及时间插件使用
2018/08/20 Javascript
js使用Promise实现简单的Ajax缓存
2018/11/14 Javascript
Echarts实现多条折线可拖拽效果
2019/12/19 Javascript
JavaScript前端实现压缩图片功能
2020/03/06 Javascript
Python简单连接MongoDB数据库的方法
2016/03/15 Python
Python首次安装后运行报错(0xc000007b)的解决方法
2016/10/18 Python
django连接mysql配置方法总结(推荐)
2018/08/18 Python
Python 实现数据结构-堆栈和队列的操作方法
2019/07/17 Python
用Python从0开始实现一个中文拼音输入法的思路详解
2019/07/20 Python
django+echart数据动态显示的例子
2019/08/12 Python
tensorflow实现将ckpt转pb文件的方法
2020/04/22 Python
Pandas之缺失数据的实现
2021/01/06 Python
美国领先的户外服装与装备用品店:Moosejaw
2016/08/25 全球购物
CK美国官网:Calvin Klein
2016/08/26 全球购物
写演讲稿所需要注意的4个条件
2014/01/09 职场文书
电厂职工自我鉴定
2014/02/20 职场文书
绩效工资实施方案
2014/03/15 职场文书
课外科技活动总结
2014/08/27 职场文书
村主任“四风”问题个人对照检查材料思想汇报
2014/10/02 职场文书
酒店人事主管岗位职责
2015/04/11 职场文书
《中国机长》观后感:敬畏生命,敬畏职责
2019/11/12 职场文书