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 相关文章推荐
在IPython中执行Python程序文件的示例
Nov 01 Python
基于Python的PIL库学习详解
May 10 Python
python 修改本地网络配置的方法
Aug 14 Python
Python识别html主要文本框过程解析
Feb 18 Python
Python3.7 读取音频根据文件名生成脚本的代码
Apr 07 Python
Python实时监控网站浏览记录实现过程详解
Jul 14 Python
Python中logging日志记录到文件及自动分割的操作代码
Aug 05 Python
Visual Studio Code搭建django项目的方法步骤
Sep 17 Python
python 利用openpyxl读取Excel表格中指定的行或列教程
Feb 06 Python
两行代码解决Jupyter Notebook中文不能显示的问题
Apr 24 Python
浅谈Python 中的复数问题
May 19 Python
pytorch中[..., 0]的用法说明
May 20 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
咖啡知识大全
2021/03/03 新手入门
Could not load type System.ServiceModel.Activation.HttpModule解决办法
2012/12/29 PHP
php 生成加密公钥加密私钥实例详解
2017/06/16 PHP
详细解读php的命名空间(二)
2018/02/21 PHP
文本链接逐个出现的js脚本
2007/12/12 Javascript
一段批量给页面上的控件赋值js
2010/06/19 Javascript
jquery重新播放css动画所遇问题解决
2013/08/21 Javascript
使用jquery hover事件实现表格的隔行换色功能示例
2013/09/03 Javascript
提高jQuery性能的十个诀窍
2013/11/14 Javascript
js语法学习之判断一个对象是否为数组
2014/05/13 Javascript
JavaScript link方法入门实例(给字符串加上超链接)
2014/10/17 Javascript
js实现感应鼠标图片透明度变化的方法
2015/02/20 Javascript
jquery控制表单输入框显示默认值的方法
2015/05/22 Javascript
jQuery带进度条全屏图片轮播特效代码分享
2020/06/28 Javascript
JavaScript设计模式初探
2016/01/07 Javascript
JS身份证信息验证正则表达式
2017/06/12 Javascript
Webpack框架核心概念(知识点整理)
2017/12/22 Javascript
每周一练 之 数据结构与算法(Stack)
2019/04/16 Javascript
vue 项目打包时样式及背景图片路径找不到的解决方式
2019/11/12 Javascript
[01:55]2014DOTA2国际邀请赛快报:国土生病 紧急去医院治疗
2014/07/10 DOTA
[01:01:22]VGJ.S vs OG 2018国际邀请赛淘汰赛BO3 第一场 8.22
2018/08/23 DOTA
[01:33]完美世界DOTA2联赛PWL S3 集锦第二期
2020/12/21 DOTA
Python中内置数据类型list,tuple,dict,set的区别和用法
2015/12/14 Python
Python使用matplotlib填充图形指定区域代码示例
2018/01/16 Python
VSCode Python开发环境配置的详细步骤
2019/02/22 Python
python ftplib模块使用代码实例
2019/12/31 Python
Pycharm打开已有项目配置python环境的方法
2020/07/03 Python
英国户外玩具儿童游乐设备网站:TP Toys(蹦床、攀爬框架、秋千、滑梯和游戏屋)
2018/04/09 全球购物
凌阳科技股份有限公司C++程序员面试题笔试题
2014/11/20 面试题
J2EE系统只能是基于web
2015/09/08 面试题
监察建议书
2015/02/04 职场文书
办公室行政主管岗位职责
2015/04/09 职场文书
社区志愿服务活动感想
2015/08/07 职场文书
给校长的建议书作文400字
2015/09/14 职场文书
一个家长教育孩子的心得体会
2016/01/15 职场文书
SQL 窗口函数实现高效分页查询的案例分析
2021/05/21 SQL Server