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实现Linux下守护进程的编写方法
Aug 22 Python
Python的Django框架中的select_related函数对QuerySet 查询的优化
Apr 01 Python
Python字符串中查找子串小技巧
Apr 10 Python
python安装与使用redis的方法
Apr 19 Python
Python彩色化Linux的命令行终端界面的代码实例分享
Jul 02 Python
Python文件与文件夹常见基本操作总结
Sep 19 Python
Python实现FTP弱口令扫描器的方法示例
Jan 31 Python
使用PyOpenGL绘制三维坐标系实例
Dec 24 Python
python logging添加filter教程
Dec 24 Python
python报错TypeError: ‘NoneType‘ object is not subscriptable的解决方法
Nov 05 Python
Python爬虫定时计划任务的几种常见方法(推荐)
Jan 15 Python
python实现银行账户系统
Feb 22 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
Laravel 5.0 发布 新版本特性详解
2015/02/10 PHP
Symfony控制层深入详解
2016/03/17 PHP
php mysql_list_dbs()函数用法示例
2017/03/29 PHP
兼容FF和IE的动态table示例自写
2013/10/21 Javascript
jquery绑定事件不生效的解决方法
2014/02/11 Javascript
jQuery实现瀑布流的取巧做法分享
2015/01/12 Javascript
JavaScript重定向URL参数的两种方法小结
2016/10/19 Javascript
关于Jquery中的bind(),on()绑定事件方式总结
2016/10/26 Javascript
jQuery实现在HTML文档加载完毕后自动执行某个事件的方法
2017/05/08 jQuery
使用Bootrap和Vue实现仿百度搜索功能
2017/10/26 Javascript
为vue项目自动设置请求状态的配置方法
2019/06/09 Javascript
详解vue 2.6 中 slot 的新用法
2019/07/09 Javascript
《javascript设计模式》学习笔记一:Javascript面向对象程序设计对象成员的定义分析
2020/04/07 Javascript
JS 逻辑判断不要只知道用 if-else 和 switch条件判断(小技巧)
2020/05/27 Javascript
vue+echarts+datav大屏数据展示及实现中国地图省市县下钻功能
2020/11/16 Javascript
[05:10]2014DOTA2国际邀请赛 通往胜利之匙赛场探秘之旅
2014/07/18 DOTA
[10:14]2018DOTA2国际邀请赛寻真——paiN Gaming不仅为自己而战
2018/08/14 DOTA
[46:50]Liquid vs Mineski 2018国际邀请赛小组赛BO2 第二场 8.18
2018/08/19 DOTA
[02:38]2018年度DOTA2最佳劣单位选手-完美盛典
2018/12/17 DOTA
python 生成目录树及显示文件大小的代码
2009/07/23 Python
Python中的XML库4Suite Server的介绍
2015/04/14 Python
python实现读取并显示图片的两种方法
2017/01/13 Python
Django 自动生成api接口文档教程
2019/11/19 Python
解决pytorch 模型复制的一些问题
2021/03/03 Python
HTML5时代CSS设置漂亮字体取代图片
2014/09/04 HTML / CSS
实习自我鉴定范文
2013/10/30 职场文书
大二学生学习个人自我评价
2014/01/19 职场文书
纪检干部个人对照检查材料
2014/09/23 职场文书
领导干部作风建设工作总结
2014/10/23 职场文书
2014年技术部工作总结
2014/12/12 职场文书
写给老婆的保证书
2015/02/27 职场文书
2015年六一儿童节演讲稿
2015/03/19 职场文书
学校开除通知书
2015/04/25 职场文书
小兵张嘎观后感
2015/06/03 职场文书
《初涉尘世》读后感3篇
2020/01/10 职场文书
MySQL 执行数据库更新update操作的时候数据库卡死了
2022/05/02 MySQL