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中的模块导入和读取键盘输入的方法
Oct 16 Python
python 写的一个爬虫程序源码
Feb 28 Python
Python 中urls.py:URL dispatcher(路由配置文件)详解
Mar 24 Python
在Python中分别打印列表中的每一个元素方法
Nov 07 Python
django+mysql的使用示例
Nov 23 Python
Python字符串和正则表达式中的反斜杠('\')问题详解
Sep 03 Python
Django实现基于类的分页功能
Oct 31 Python
Python collections中的双向队列deque简单介绍详解
Nov 04 Python
python3中pip3安装出错,找不到SSL的解决方式
Dec 12 Python
python使用html2text库实现从HTML转markdown的方法详解
Feb 21 Python
Python+OpenCV实现图像的全景拼接
Mar 05 Python
在NumPy中深拷贝和浅拷贝相关操作的定义和背后的原理
Apr 14 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
php Static关键字实用方法
2010/06/04 PHP
php中关于codeigniter的xmlrpc的类在进行数据交换时的类型问题
2011/07/03 PHP
php判断ip黑名单程序代码实例
2014/02/24 PHP
php使用gearman进行任务分发操作实例详解
2020/02/26 PHP
one.php 多项目、函数库、类库 统一为一个版本的方法
2020/08/24 PHP
JavaScript 组件之旅(二)编码实现和算法
2009/10/28 Javascript
javascript 静态对象和构造函数的使用和公私问题
2010/03/02 Javascript
jquery 实现窗口的最大化不论什么情况
2013/09/03 Javascript
jquery及原生js获取select下拉框选中的值示例
2013/10/25 Javascript
EasyUI,点击开启编辑框,并且编辑框获得焦点的方法
2015/03/01 Javascript
js实现简洁大方的二级下拉菜单效果代码
2015/09/01 Javascript
AngularJs中route的使用方法和配置
2016/02/04 Javascript
jQuery Mobile开发中日期插件Mobiscroll使用说明
2016/03/02 Javascript
jQuery解析与处理服务器端返回xml格式数据的方法详解
2016/07/04 Javascript
nodejs爬虫遇到的乱码问题汇总
2017/04/07 NodeJs
JS中的数组转变成JSON格式字符串的方法
2017/05/09 Javascript
nodejs制作爬虫实现批量下载图片
2017/05/19 NodeJs
纯JS实现简单的日历
2017/06/26 Javascript
JS按条件 serialize() 对应标签的使用方法
2017/07/24 Javascript
jQuery 禁止表单用户名、密码自动填充功能
2017/10/30 jQuery
Angular2的管道Pipe的使用方法
2017/11/07 Javascript
vue项目中使用tinymce编辑器的步骤详解
2018/09/11 Javascript
javacript replace 正则取字符串中的值并替换【推荐】
2018/09/13 Javascript
Vue axios 跨域请求无法带上cookie的解决
2020/09/08 Javascript
[58:54]EG vs RNG 2019国际邀请赛小组赛 BO2 第一场 8.16
2019/08/18 DOTA
Python装饰器基础详解
2016/03/09 Python
解决python给列表里添加字典时被最后一个覆盖的问题
2019/01/21 Python
python的内存管理和垃圾回收机制详解
2019/05/18 Python
python paramiko远程服务器终端操作过程解析
2019/12/14 Python
Pycharm快捷键配置详细整理
2020/10/13 Python
Python实现简单的猜单词小游戏
2020/10/28 Python
浅析两列自适应布局的3种思路
2016/05/03 HTML / CSS
俄罗斯金苹果网上化妆品和香水商店:Goldapple
2019/12/01 全球购物
经典c++面试题二
2015/08/14 面试题
餐饮部总监岗位职责范文
2014/02/13 职场文书
2014年宣传思想工作总结
2014/12/10 职场文书