Python函数装饰器常见使用方法实例详解


Posted in Python onMarch 30, 2019

本文实例讲述了Python函数装饰器常见使用方法。分享给大家供大家参考,具体如下:

一、装饰器

首先,我们要了解到什么是开放封闭式原则?

软件一旦上线后,对修改源代码是封闭的,对功能的扩张是开放的,所以我们应该遵循开放封闭的原则。

也就是说:我们必须找到一种解决方案,能够在不修改一个功能源代码以及调用方式的前提下,为其加上新功能。

总结:原则如下:

1、不修改源代码
2、不修改调用方式

目的:在遵循1和2原则的基础上扩展新功能。

二、什么是装饰器?

器:指的是工具,

装饰:指的是为被装饰对象添加新功能。

完整的含义:装饰器即在不修改装饰对象源代码与调用方式的前提下,为被装饰器对象添加新功能的一种函数,这个函数的特殊之处就在于它的返回值也是一个函数。

一般而言,我们想要拓展原来函数的代码,直接的办法就是侵入代码里面修改,例如:

import time
def index():
  start_time=time.time()
  time.sleep(3)
  print('hello word')
  stop_time=time.time()
  print('run time is %s'%(stop_time-start_time))
index()

输出:

hello word
run time is 3.000171422958374

以上代码就是让你过三秒才打印'hello word',下面我们要再添加一个新功能,和上面的功能一样,但是要传参数进去,过5秒输出结果。

修改1:

import time
def index():
  time.sleep(3)
  print('hello word')
def home(name):
  time.sleep(5)
  print('welcome %s to home page'%name)
def wrapper(func):
  start_time=time.time()
  func('haolo')
  stop_time=time.time()
  print('run time is %s'%(stop_time-start_time))
wrapper(home)

输出:

welcome haolo to home page
run time is 5.000286102294922

这样写感觉还是不怎么好,而且我们还修改了函数的调用方式,很不符合规矩。所以我们还是换一种方式来修改它。通过装饰器的方式。

修改2

import time
def index():
  time.sleep(3)
  print('hello word')
def home(name):
  time.sleep(5)
  print('welcome to %s'%name)
def outter(func): # func为最原始的inde 和home
  def warpper():
    start_time=time.time()
    func('yuan')
    stop_time=time.time()
    print(stop_time-start_time)
  return warpper
home=outter(home) ###home这个变量名是新赋值的,把原来的home给覆盖了。
home()

输出:

welcome to yuan
5.000286102294922

这种方式虽然满足了不修改源代码和不修改调用方式的条件,但还是不能够实现两个函数同时运行的功能,说到底还是不行,我们还得想个方式出来。就是让他们两个同时运行。这时,我又想到了上节课所学的知识,就是*args**kargs,用两个函数通过可变参数形式来实现内嵌函数的形式传入,所以它支持运行是构建参数列表,这对于以上两次不能解决的办法是最有效的。下面我们来试试,看到底能不能成功。

方式3:

import time
def index():
  time.sleep(3)
  print('hello word')
def home(name):
  time.sleep(5)
  print('welcome %s to home page'%name)
def timmer(func):  #func为最原始的home
  def warpper(*args,**kwargs):
    start_time=time.time()
    res=func(*args,**kwargs) #调用了最原始的home
    stop_time=time.time()
    print(stop_time-start_time)
    return res
  return warpper
index=timmer(index) #为最新的index = wrapper
home=timmer(home) #为最新的home = wrapper
home(name='yuan') #wrapper=('yuan')
index() #wrapper

输出:

welcome yuan to home page
5.000285863876343
hello word
3.000171661376953

看吧,很快就实现了两个功能并用,而且我们还没有修改原始代码,还有调用方式。

其实很简单,我只是用了一个无参装饰器的模板,这个模板可以说是万能的,在以后很多的函数代码都可以用这种方式来套用。

模板:

def outer(func):
  def inner(*args,**kwargs):
    res=func(*args,**kwargs)
    return res
  return inner

现在又有问题来了,我们调装饰器的时候,每调一次,又要把装饰器对象传进来,调一次又传一次,这样不会觉得很麻烦吗?那么我们又想到了一种方法,就是装饰器语法糖,在被装饰对象的上面加@timmer 用它来取代 index=timmer(index)

并且把返回值正常的返回给它。

import time
def timmer(func):  #func为最原始的home
  def warpper(*args,**kwargs):
    start_time=time.time()
    res=func(*args,**kwargs) #调用了最原始的home
    stop_time=time.time()
    print(stop_time-start_time)
    return res
  return warpper
@timmer #就是取代底下的 index=timmer(index)
def index():
  time.sleep(3)
  print('hello word')
@timmer #就是取代底下的home=timmer(home)  home(name='yuan')
def home(name):
  time.sleep(5)
  print('welcome %s to home page'%name)
index()
home('liuyuan')

输出:

hello word
3.000171661376953
welcome liuyuan to home page
5.000286102294922

注意:这里的timmer函数就是最原始的装饰器,它的参数就是一个函数,然后返回值也是一个函数。其中作为参数的这个函数index()hemo(name)就是在返回函数的wrapper()的内部执行。然后再这两个函数的前面加上@timmerindex()home(name)函数就相当于被注入了计时功能,现在只需要调用index()home('yuan'),它就已经变身为'新功能更多的函数了。'

所以这里的装饰器就像一个注入的符号:有了它,拓展了原来函数的功能既不需要侵入函数内更改代码,也不需要重复执行原函数。

用装饰器来实现认证功能:

import time
current_user={
  'username':None,
  #'login_time':None
}
def auth(func):
  # func = index
  def wrapper(*args,**kwargs):
    if current_user['username']:
      print('已经登录过了')
      res=func(*args,**kwargs)
      return res
    uname = input('输入用户名:').strip()
    pwd = input('密码:').strip()
    if uname == 'yuan' and pwd == '123':
      print('登录成功')
      current_user['username']=uname
      res = func(*args,**kwargs)
      return res
    else:
      print('用户名或密码错误')
  return wrapper
@auth #index = auth(index)
def index():
  time.sleep(1)
  print('welcom to index page')
@auth
def home(name):
  time.sleep(2)
  print('welcome %s to home page'%name)
input()
home('yuan')

有参数的装饰器来用于用户认证

import time
current_user={
  'username':None,
  # 'login_time':None
}
def auth(func):
  # func=index
  def wrapper(*args,**kwargs):
    if current_user['username']:
      print('已经登陆过了')
      res=func(*args,**kwargs)
      return res
    uname=input('用户名>>: ').strip()
    pwd=input('密码>>: ').strip()
    if uname == 'yuan' and pwd == '123':
      print('登陆成功')
      current_user['username']=uname
      res=func(*args,**kwargs)
      return res
    else:
      print('用户名或密码错误')
  return wrapper
def timmer(func):
  def wrapper(*args,**kwargs):
    start_time=time.time()
    res=func(*args,**kwargs)
    stop_time=time.time()
    print(stop_time-start_time)
    return res
  return wrapper
@timmer # timmer 统计的是auth+index的执行时间
@auth
def index():
  time.sleep(1)
  print('welcome to index page')
  return 122
index()

叠加多个装饰器:

import time
current_user={
  'username':None,
  # 'login_time':None
}
def auth(func):
  # func=index
  def wrapper(*args,**kwargs):
    if current_user['username']:
      print('已经登陆过了')
      res=func(*args,**kwargs)
      return res
    uname=input('用户名>>: ').strip()
    pwd=input('密码>>: ').strip()
    if uname == 'egon' and pwd == '123':
      print('登陆成功')
      current_user['username']=uname
      res=func(*args,**kwargs)
      return res
    else:
      print('用户名或密码错误')
  return wrapper
def timmer(func):
  def wrapper(*args,**kwargs):
    start_time=time.time()
    res=func(*args,**kwargs)
    stop_time=time.time()
    print(stop_time-start_time)
    return res
  return wrapper
@timmer # timmer 统计的是auth+index的执行时间
@auth
def index():
  time.sleep(1)
  print('welcome to index page')
  return 122
index()

关于Python相关内容感兴趣的读者可查看本站专题:《Python函数使用技巧总结》、《Python面向对象程序设计入门与进阶教程》、《Python数据结构与算法教程》、《Python字符串操作技巧汇总》、《Python编码操作技巧总结》及《Python入门与进阶经典教程》

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

Python 相关文章推荐
Django1.3添加app提示模块不存在的解决方法
Aug 26 Python
Python的shutil模块中文件的复制操作函数详解
Jul 05 Python
wxPython之解决闪烁的问题
Jan 15 Python
高效使用Python字典的清单
Apr 04 Python
解决Python print 输出文本显示 gbk 编码错误问题
Jul 13 Python
python 接收处理外带的参数方法
Dec 03 Python
Python提取转移文件夹内所有.jpg文件并查看每一帧的方法
Jun 27 Python
Python使用pyserial进行串口通信的实例
Jul 02 Python
如何提高python 中for循环的效率
Apr 15 Python
基于SQLAlchemy实现操作MySQL并执行原生sql语句
Jun 10 Python
sqlalchemy实现时间列自动更新教程
Sep 02 Python
Python unittest装饰器实现原理及代码
Sep 08 Python
Python函数基础实例详解【函数嵌套,命名空间,函数对象,闭包函数等】
Mar 30 #Python
Python函数的参数常见分类与用法实例详解
Mar 30 #Python
Python实现定时执行任务的三种方式简单示例
Mar 30 #Python
详解Python解决抓取内容乱码问题(decode和encode解码)
Mar 29 #Python
详解python读取和输出到txt
Mar 29 #Python
Python实现账号密码输错三次即锁定功能简单示例
Mar 29 #Python
详解Python函数式编程—高阶函数
Mar 29 #Python
You might like
在同一窗体中使用PHP来处理多个提交任务
2006/10/09 PHP
PHP输出英文时间日期的安全方法(RFC 1123格式)
2014/06/13 PHP
PHP中使用php5-ffmpeg撷取视频图片实例
2015/01/07 PHP
php解析mht文件转换成html的实例
2017/03/13 PHP
SWFObject 2.1以上版本语法介绍
2010/07/10 Javascript
各浏览器中querySelector和querySelectorAll的实现差异分析
2012/05/23 Javascript
js获取GridView中行数据的两种方法 分享
2013/07/13 Javascript
javascript定义变量时带var与不带var的区别分析
2015/01/12 Javascript
基于JQuery实现的跑马灯效果(文字无缝向上翻动)
2016/12/02 Javascript
socket.io实现在线群聊功能
2017/04/07 Javascript
Vue.use源码分析
2017/04/22 Javascript
jQuery遍历节点方法汇总(推荐)
2017/05/13 jQuery
JS和Canvas实现图片的预览压缩和上传功能
2018/03/30 Javascript
JavaScript实现五子棋游戏的方法详解
2019/07/08 Javascript
[06:43]2018DOTA2国际邀请赛寻真——VGJ.Thunder
2018/08/11 DOTA
Python实现的金山快盘的签到程序
2013/01/17 Python
使用Python编写一个模仿CPU工作的程序
2015/04/16 Python
玩转python爬虫之正则表达式
2016/02/17 Python
Python使用正则表达式实现文本替换的方法
2017/04/18 Python
浅谈numpy生成数组的零值问题
2018/11/12 Python
python basemap 画出经纬度并标定的实例
2019/07/09 Python
Python 安装 virturalenv 虚拟环境的教程详解
2020/02/21 Python
css3 伪元素和伪类选择器详解
2014/09/04 HTML / CSS
AHAVA美国官方网站:死海海泥护肤品牌
2016/10/18 全球购物
香港网上花店:FlowerAdvisor香港
2019/05/30 全球购物
印度手工编织服装和家居用品商店:Fabindi
2019/10/07 全球购物
跟单文员岗位职责
2014/01/03 职场文书
初中生物教学反思
2014/01/10 职场文书
2014年最新学习全国两会精神心得
2014/03/17 职场文书
白酒营销策划方案
2014/08/17 职场文书
坚守艰苦奋斗精神坚决反对享乐主义整改措施
2014/09/17 职场文书
学习十八大标语
2014/10/09 职场文书
贷款工作证明模板
2015/06/12 职场文书
深度学习tensorflow基础mnist
2021/04/14 Python
Django使用echarts进行可视化展示的实践
2021/06/10 Python
python 判断字符串当中是否包含字符(str.contain)
2022/06/01 Python