Python装饰器(decorator)定义与用法详解


Posted in Python onFebruary 09, 2018

本文实例讲述了Python装饰器(decorator)定义与用法。分享给大家供大家参考,具体如下:

什么是装饰器(decorator)

简单来说,可以把装饰器理解为一个包装函数的函数,它一般将传入的函数或者是类做一定的处理,返回修改之后的对象.所以,我们能够在不修改原函数的基础上,在执行原函数前后执行别的代码.比较常用的场景有日志插入,事务处理等.

装饰器

最简单的函数,返回两个数的和

def calc_add(a, b):
 return a + b
calc_add(1, 2)

但是现在又有新的需求,计算求和操作耗时,很简单,求和前获取一下时间,求和后再获取一次,求差即可

import datetime
def calc_add(a, b):
 start_time = datetime.datetime.now()
 result = a + b
 end_tiem = datetime.datetime.now()
 print "result:", result, "used:", (end_tiem - start_time).microseconds, "μs"
 return result
calc_add(1, 2)

现在呢,函数calc_diff(a, b),计算a-b,也想计算减法操作的时间差,很好办,把那段代码复制过去.但是假如我们现在想编的是一个数学函数库,各种函数都想计算其执行耗时,总不能一个一个复制代码,想个更好的办法.

我们知道,在Python中函数也是被视为对象的,可以作为参数传递,那么假如把计算耗时的独立为一个单独的函数calc_spend_time(),然后把需要计算耗时的函数例如calc_add的引用传递给它,在calc_spend_time中调用calc_add,这样所有的需要计算耗时的函数都不用修改自己的代码了.

def calc_spend_time(func, *args, **kargs):
 start_time = datetime.datetime.now()
 result = func(*args, **kargs)
 end_tiem = datetime.datetime.now()
 print "result:", result, "used:", (end_tiem - start_time).microseconds, "μs"
def calc_add(a, b):
 return a + b
calc_spend_time(calc_add, 1, 1)
# calc_spend_time(calc_add, a=1, b=2)

看起来也不错,负责计算的函数不用更改,只需调用的时候作为参数传给计算时间差的函数.但就是这,调用的时候形式变了,不再是clac(1, 2),而是calc_spend_time(clac_add, 1, 2),万一calc_add大规模被调用,那么还得一处一处找,然后修改过来,还是很麻烦.如果想不修改代码,就得使clac()calc_spend_time(clac)效果一样,那么可以在calc_spend_time()里把传入的clac包装一下,然后返回包装后的新的函数,再把返回的包装好的函数赋给clac,那么calc()的效果就和上例calc_spend_time(calc())效果一样.

import datetime
def calc_spend_time(func):
 def new_func(a, b):
  start_time = datetime.datetime.now()
  result = func(a, b)
  end_tiem = datetime.datetime.now()
  print "result:", result, "used:", (end_tiem - start_time).microseconds, "μs"
 return new_func
def calc_add(a, b):
 return a + b
calc_add = calc_spend_time(calc_add)
calc_add(1, 2)

语法糖

上面的例子就是装饰器的概念,包装函数的函数.事实上上面的例子还可以更精简

import datetime
def calc_spend_time(func):
 def new_func(a, b):
  start_time = datetime.datetime.now()
  result = func(a, b)
  end_tiem = datetime.datetime.now()
  print "result:", result, "used:", (end_tiem - start_time).microseconds, "μs"
 return new_func
@calc_spend_time
def calc_add(a, b):
 return a + b
calc_add(1, 2)

@calc_spend_time就是语法糖,它的本质就是:calc_add = calc_spend_time(calc_add)

无参数的函数装饰器

import datetime
def calc_spend_time(func):
 def new_func(*args, **kargs):
  start_time = datetime.datetime.now()
  result = func(*args, **kargs)
  end_tiem = datetime.datetime.now()
  print "result:", result, "used:", (end_tiem - start_time).microseconds, "μs"
 return new_func
@calc_spend_time
def calc_add(a, b):
 return a + b
@calc_spend_time
def calc_diff(a, b):
 return a - b
calc_add(a=1, b=2)
calc_diff(1, 2)

注:

*args:把所有的参数按出现顺序打包成list
**kargs:把所有的key=value形式的参数打包成一个dict

带参数的函数装饰器

假如我们需要知道函数的一些额外信息,例如函数作者,可以通过给装饰器函数增加参数来实现.

import datetime
def calc_spend_time(author):
 def first_deco(func):
  def new_func(*args, **kargs):
   start_time = datetime.datetime.now()
   result = func(*args, **kargs)
   end_tiem = datetime.datetime.now()
   print author, "result:", result, "used:", (end_tiem - start_time).microseconds, "μs"
  return new_func
 return first_deco
@calc_spend_time('author_1')
def calc_add(a, b):
 return a + b
@calc_spend_time('author_2')
def calc_diff(a, b):
 return a - b
calc_add(a=1, b=2)
calc_diff(1, 2)

Python内置装饰器

Python内置的装饰器有三个:staticmethodclassmethodproperty

staticmethod:把类中的方法定义为静态方法,使用staticmethod装饰的方法可以使用类或者类的实例对象来调用,不需要传入self

class Human(object):
 """docstring for Human"""
 def __init__(self):
  super(Human, self).__init__()
 @staticmethod
 def say(message):
  if not message:
   message = 'hello'
  print 'I say %s' % message
 def speak(self, message):
  self.say(message)
Human.say(None)
human = Human()
human.speak('hi')

输出:

I say hello
I say hi

classmethod:把类中的方法定义为类方法,使用classmethod装饰的方法可以使用类或者类的实例对象来调用,并将该class对象隐式的作为第一个参数传入

class Human(object):
 """docstring for Human"""
 def __init__(self):
  super(Human, self).__init__()
  self.message = '111'
 def say(message):
  if not message:
   message = 'hello'
  print 'I say %s' % message
 @classmethod
 def speak(cls, message):
  if not message:
   message = 'hello'
  cls.say(message)
human = Human()
human.speak('hi')

输出同上例

property:把方法变成属性

class Human(object):
 """docstring for Human"""
 def __init__(self, value):
  super(Human, self).__init__()
  self._age = value
 @property
 def age(self):
  return self._age
human = Human(20)
print human.age

更多关于Python相关内容可查看本站专题:《Python数据结构与算法教程》、《Python Socket编程技巧总结》、《Python函数使用技巧总结》、《Python字符串操作技巧汇总》及《Python入门与进阶经典教程》

希望本文所述对大家Python程序设计有所帮助。

Python 相关文章推荐
Python深入学习之装饰器
Aug 31 Python
python根据开头和结尾字符串获取中间字符串的方法
Mar 26 Python
Python算法应用实战之队列详解
Feb 04 Python
numpy.random.seed()的使用实例解析
Feb 03 Python
python用户管理系统
Mar 13 Python
详解opencv Python特征检测及K-最近邻匹配
Jan 21 Python
Python选择网卡发包及接收数据包
Apr 04 Python
基于Django ORM、一对一、一对多、多对多的全面讲解
Jul 26 Python
一文了解python 3 字符串格式化 F-string 用法
Mar 04 Python
Django模板标签中url使用详解(url跳转到指定页面)
Mar 19 Python
Python中Selenium库使用教程详解
Jul 23 Python
python中watchdog文件监控与检测上传功能
Oct 30 Python
详解python的ORM中Pony用法
Feb 09 #Python
python监控键盘输入实例代码
Feb 09 #Python
Python with语句上下文管理器两种实现方法分析
Feb 09 #Python
Python遍历pandas数据方法总结
Feb 09 #Python
python中的闭包函数
Feb 09 #Python
基于Python socket的端口扫描程序实例代码
Feb 09 #Python
利用python 更新ssh 远程代码 操作远程服务器的实现代码
Feb 08 #Python
You might like
phpinfo 系统查看参数函数代码
2009/06/05 PHP
在windows平台上构建自己的PHP实现方法(仅适用于php5.2)
2013/07/05 PHP
php 反斜杠处理函数addslashes()和stripslashes()实例详解
2016/12/25 PHP
node爬取微博的数据的简单封装库nodeweibo使用指南
2015/01/02 Javascript
JavaScript图像延迟加载库Echo.js
2016/04/05 Javascript
jquery获取复选框checkbox的值实现方法
2016/05/30 Javascript
AngularJS 中的事件详解
2016/07/28 Javascript
用js控件div的滚动条,让它在内容更新时自动滚到底部的实现方法
2016/10/27 Javascript
详解PHP中pathinfo()函数导致的安全问题
2017/01/05 Javascript
JS多文件上传的实例代码
2017/01/11 Javascript
jquery ajax异步提交表单数据的方法
2017/10/27 jQuery
vue中使用input[type="file"]实现文件上传功能
2018/09/10 Javascript
Vue-axios-post数据后端接不到问题解决
2020/01/09 Javascript
[46:23]完美世界DOTA2联赛PWL S2 FTD vs Magma 第一场 11.20
2020/11/23 DOTA
用python写asp详细讲解
2013/12/16 Python
Python使用urllib模块的urlopen超时问题解决方法
2014/11/08 Python
Python中的默认参数详解
2015/06/24 Python
Python守护进程和脚本单例运行详解
2017/01/06 Python
TensorFlow实现简单卷积神经网络
2018/05/24 Python
numpy下的flatten()函数用法详解
2019/05/27 Python
python爬虫开发之PyQuery模块详细使用方法与实例全解
2020/03/09 Python
Django添加bootstrap框架时无法加载静态文件的解决方式
2020/03/27 Python
Jupyter Notebook 实现正常显示中文和负号
2020/04/24 Python
利用OpenCV中对图像数据进行64F和8U转换的方式
2020/06/03 Python
瑞典领先的汽车零部件网上零售商:bildelaronline24.se
2017/01/12 全球购物
戴森台湾线上商城:Dyson Taiwan
2018/05/21 全球购物
周生生珠宝香港官网:Chow Sang Sang(香港及海外配送)
2019/09/05 全球购物
电子商务个人职业生涯规划范文
2014/02/12 职场文书
致标枪运动员加油稿
2014/02/15 职场文书
舞蹈专业求职信
2014/06/13 职场文书
县政府办公室领导班子对照检查材料思想汇报
2014/09/28 职场文书
党的群众路线教育实践活动个人对照检查材料(公安)
2014/11/05 职场文书
失恋33天观后感
2015/06/11 职场文书
《猴王出世》教学反思
2016/02/23 职场文书
如何创建一个创建MySQL数据库中的datetime类型
2022/03/21 MySQL
Python识别花卉种类鉴定网络热门植物并自动整理分类
2022/04/08 Python