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 相关文章推荐
Python用UUID库生成唯一ID的方法示例
Dec 15 Python
python 计算两个日期相差多少个月实例代码
May 24 Python
Python3安装Pymongo详细步骤
May 26 Python
django 修改server端口号的方法
May 14 Python
如何在django里上传csv文件并进行入库处理的方法
Jan 02 Python
python 实现创建文件夹和创建日志文件的方法
Jul 07 Python
50行Python代码实现视频中物体颜色识别和跟踪(必须以红色为例)
Nov 20 Python
pycharm快捷键汇总
Feb 14 Python
在ipython notebook中使用argparse方式
Apr 20 Python
Python填充任意颜色,不同算法时间差异分析说明
May 16 Python
PyCharm最新激活码PyCharm2020.2.3有效
Nov 18 Python
OpenCV-Python实现人脸磨皮算法
Jun 07 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
关于手调机和数调机的选择
2021/03/02 无线电
isset和empty的区别
2007/01/15 PHP
jquery ajax 同步异步的执行示例代码
2010/06/23 Javascript
A标签中通过href和onclick传递的this对象实现思路
2013/04/19 Javascript
简述JavaScript中正则表达式的使用方法
2015/06/15 Javascript
jQuery查找和过滤_动力节点节点Java学院整理
2017/07/04 jQuery
JavaScript框架Angular和React深度对比
2017/11/20 Javascript
Angular父组件调用子组件的方法
2018/04/02 Javascript
深入浅出理解JavaScript闭包的功能与用法
2018/08/01 Javascript
webpack配置proxyTable时pathRewrite无效的解决方法
2018/12/13 Javascript
react中使用css的7中方式(最全总结)
2019/02/11 Javascript
详解Vue用cmd创建项目
2019/02/12 Javascript
微信小程序商品详情页底部弹出框
2019/11/22 Javascript
在vue中给后台接口传的值为数组的格式代码
2020/11/12 Javascript
Python 元组(Tuple)操作详解
2014/03/11 Python
Jupyter安装nbextensions,启动提示没有nbextensions库
2020/04/23 Python
解决python ogr shp字段写入中文乱码的问题
2018/12/31 Python
解决新版Pycharm中Matplotlib图像不在弹出独立的显示窗口问题
2019/01/15 Python
24式加速你的Python(小结)
2019/06/13 Python
500行代码使用python写个微信小游戏飞机大战游戏
2019/10/16 Python
python定时截屏实现
2020/11/02 Python
IE9下html5初试小刀
2010/09/21 HTML / CSS
Tripadvisor新西兰:阅读评论,比较价格和酒店预订
2018/02/10 全球购物
linux面试题参考答案(11)
2016/11/26 面试题
初任培训自我鉴定
2013/10/07 职场文书
外贸销售员求职的自我评价
2013/11/23 职场文书
小饰品店的创业计划书范文
2013/12/28 职场文书
酒店个人求职信范文
2014/01/25 职场文书
企业厂务公开实施方案
2014/03/26 职场文书
旅游安全协议书
2014/04/21 职场文书
2014年质检员工作总结
2014/11/18 职场文书
家庭经济困难证明
2015/06/23 职场文书
2015七夕情人节宣传语
2015/07/14 职场文书
校长新学期寄语2016
2015/12/04 职场文书
《原神》新角色演示“神里绫人:林隐泓洄” 宠妹狂魔
2022/04/03 其他游戏
Android 中的类文件和类加载器详情
2022/06/05 Java/Android