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中模拟enum枚举类型的5种方法分享
Nov 22 Python
Python的Django框架安装全攻略
Jul 15 Python
Python使用迭代器打印螺旋矩阵的思路及代码示例
Jul 02 Python
Python 实现简单的shell sed替换功能(实例讲解)
Sep 29 Python
Python实现读取json文件到excel表
Nov 18 Python
Python批量提取PDF文件中文本的脚本
Mar 14 Python
python实现图片识别汽车功能
Nov 30 Python
Python的UTC时间转换讲解
Feb 26 Python
python获取依赖包和安装依赖包教程
Feb 13 Python
基于keras中的回调函数用法说明
Jun 17 Python
C++和python实现阿姆斯特朗数字查找实例代码
Dec 07 Python
python tkinter模块的简单使用
Apr 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
150kHz到30Mhz完全冲浪手册
2020/03/20 无线电
一个可查询所有表的“通用”查询分页类
2006/10/09 PHP
php5.2.0内存管理改进
2007/01/22 PHP
令PHP初学者头疼十四条问题大总结
2008/11/12 PHP
DW中链接mysql数据库时,建立字符集中文出现乱码的解决方法
2010/03/27 PHP
PHP读取网页文件内容的实现代码(fopen,curl等)
2011/06/23 PHP
php将数组转换成csv格式文件输出的方法
2015/03/14 PHP
php实现的xml操作类
2016/01/15 PHP
PHP读MYSQL中文乱码的快速解决方法
2016/10/01 PHP
javascript Zifa FormValid 0.1表单验证 代码打包下载
2007/06/08 Javascript
地震发生中逃生十大法则
2008/05/12 Javascript
jQuery操作表格(table)的常用方法、技巧汇总
2014/04/12 Javascript
跟我学习javascript的定时器
2015/11/19 Javascript
js针对ip地址、子网掩码、网关的逻辑性判断
2016/01/06 Javascript
基于JavaScript如何制作遮罩层对话框
2016/01/26 Javascript
通过正则表达式获取url中参数的简单实现
2016/06/07 Javascript
JS弹出新窗口被拦截的解决方法
2016/08/09 Javascript
AngularJS解决ng-if中的ng-model值无效的问题
2017/06/21 Javascript
Vue.js实现一个todo-list的上移下移删除功能
2017/06/26 Javascript
layui表格数据复选框回显设置方法
2019/09/13 Javascript
[02:56]DOTA2英雄基础教程 巨魔战将
2013/12/10 DOTA
Linux中安装Python的交互式解释器IPython的教程
2016/06/13 Python
音频处理 windows10下python三方库librosa安装教程
2020/06/20 Python
python多线程semaphore实现线程数控制的示例
2020/08/10 Python
简单总结CSS3中视窗单位Viewport的常见用法
2016/02/04 HTML / CSS
乌克兰网上服装店:Bolf.ua
2018/10/30 全球购物
Ajax实现页面无刷新留言效果
2021/03/24 Javascript
中药专业大学生医药工作求职信
2013/10/25 职场文书
高级工程师英文求职信
2014/03/19 职场文书
教师节活动主持词
2014/04/02 职场文书
铣床操作工岗位职责
2014/06/13 职场文书
公务员培的训心得体会
2014/09/01 职场文书
街道党工委党的群众路线教育实践活动对照检查材料思想汇报
2014/10/05 职场文书
就业推荐表导师评语
2014/12/31 职场文书
酒店收银员岗位职责
2015/04/07 职场文书
工作失职检讨书范文
2015/05/05 职场文书