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检测手机QQ在线状态的脚本代码
Feb 10 Python
Python 命令行非阻塞输入的小例子
Sep 27 Python
在Python中使用pngquant压缩png图片的教程
Apr 09 Python
深入解析Python中的descriptor描述器的作用及用法
Jun 27 Python
python 全文检索引擎详解
Apr 25 Python
设置python3为默认python的方法
Oct 31 Python
pandas数据集的端到端处理
Feb 18 Python
Python面向对象程序设计多继承和多态用法示例
Apr 08 Python
详解Python 爬取13个旅游城市,告诉你五一大家最爱去哪玩?
May 07 Python
tensorboard显示空白的解决
Feb 15 Python
Python如何实现远程方法调用
Aug 07 Python
python性能测试工具locust的使用
Dec 28 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
全国FM电台频率大全 - 15 山东省
2020/03/11 无线电
ninety plus是什么?ninety plus咖啡好吗?
2021/03/04 新手入门
PHP 字符串 小常识
2009/06/05 PHP
php+ajax实现无刷新分页的方法
2014/11/04 PHP
php模仿qq空间或朋友圈发布动态、评论动态、回复评论、删除动态或评论的功能(中)
2017/06/11 PHP
PHP基于redis计数器类定义与用法示例
2018/02/08 PHP
PHP通过文件保存和更新信息的方法分析
2019/09/12 PHP
setTimeout 不断吐食CPU的问题分析
2009/04/01 Javascript
jquery判断字符输入个数(数字英文长度记为1,中文记为2,超过长度自动截取)
2010/10/15 Javascript
js中substring和substr的详细介绍与用法
2013/08/29 Javascript
JavaScript设计模式之工厂模式和构造器模式
2015/02/11 Javascript
jQuery插件PageSlide实现左右侧栏导航菜单
2015/04/12 Javascript
JavaScript实现选择框按比例拖拉缩放的方法
2015/08/04 Javascript
jQuery实现的仿百度分页足迹效果代码
2015/10/30 Javascript
jQuery实现非常实用漂亮的select下拉菜单选择效果
2015/11/06 Javascript
微信小程序 网络API发起请求详解
2016/11/09 Javascript
Vue.js实现一个漂亮、灵活、可复用的提示组件示例
2017/03/17 Javascript
详解打造 Vue.js 可复用组件
2017/03/24 Javascript
vee-validate vue 2.0自定义表单验证的实例
2018/08/28 Javascript
详解nodejs 配置文件处理方案
2019/01/02 NodeJs
js中async函数结合promise的小案例浅析
2019/04/14 Javascript
antd日期选择器禁止选择当天之前的时间操作
2020/10/29 Javascript
[05:53]完美世界携手游戏风云打造 卡尔工作室观战系统篇
2013/04/22 DOTA
python调用短信猫控件实现发短信功能实例
2014/07/04 Python
跟老齐学Python之Import 模块
2014/10/13 Python
[原创]pip和pygal的安装实例教程
2017/12/07 Python
解析Python中的eval()、exec()及其相关函数
2017/12/20 Python
python导出chrome书签到markdown文件的实例代码
2017/12/27 Python
解决python ogr shp字段写入中文乱码的问题
2018/12/31 Python
TensorBoard 计算图的查看方式
2020/02/15 Python
解决Python paramiko 模块远程执行ssh 命令 nohup 不生效的问题
2020/07/14 Python
模具设计与制造专业自荐书
2014/07/01 职场文书
民政工作个人总结
2015/02/28 职场文书
治庸问责工作总结
2015/08/11 职场文书
Python实战之实现康威生命游戏
2021/04/26 Python
详解MySQL主从复制及读写分离
2021/05/07 MySQL