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读取Android permission文件
Nov 01 Python
用Python实现QQ游戏大家来找茬辅助工具
Sep 14 Python
Python中如何优雅的合并两个字典(dict)方法示例
Aug 09 Python
Python 使用PIL numpy 实现拼接图片的示例
May 08 Python
Django框架使用富文本编辑器Uedit的方法分析
Jul 31 Python
Python3爬取英雄联盟英雄皮肤大图实例代码
Nov 14 Python
对python pandas读取剪贴板内容的方法详解
Jan 24 Python
django认证系统实现自定义权限管理的方法
Aug 28 Python
Python调用接口合并Excel表代码实例
Mar 31 Python
Python中格式化字符串的四种实现
May 26 Python
解决Python3.7.0 SSL低版本导致Pip无法使用问题
Sep 03 Python
FP-growth算法发现频繁项集——构建FP树
Jun 24 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求小于1000的所有水仙花数的代码
2012/01/10 PHP
ThinkPHP采用GET方式获取中文参数查询无结果的解决方法
2014/06/26 PHP
Yii框架在页面输出执行sql语句以方便调试的实现方法
2014/12/24 PHP
eclipse php wamp配置教程
2016/06/30 PHP
php生成mysql的数据字典
2016/07/07 PHP
php实现HTML实体编号与非ASCII字符串相互转换类实例
2016/11/02 PHP
js利用Array.splice实现Array的insert/remove
2009/01/13 Javascript
Javascript中的window.event.keyCode使用介绍
2011/04/26 Javascript
JQUERY 实现窗口滚动搜索框停靠效果(类似滚动停靠)
2013/03/27 Javascript
js将long日期格式转换为标准日期格式实现思路
2013/04/07 Javascript
js使用递归解析xml
2014/12/12 Javascript
javascript结合CSS实现苹果开关按钮特效
2015/04/07 Javascript
JavaScript数组去重的3种方法和代码实例
2015/07/01 Javascript
AngularJS基础 ng-selected 指令简单示例
2016/08/03 Javascript
折叠菜单及选择器的运用
2017/02/03 Javascript
使用angular帮你实现拖拽的示例
2017/07/05 Javascript
vue-router之nuxt动态路由设置的两种方法小结
2018/09/26 Javascript
vue2.0中set添加属性后视图不能更新的解决办法
2019/02/22 Javascript
layer弹出层取消遮罩的方法
2019/09/25 Javascript
layui点击左侧导航栏,实现不刷新整个页面,只刷新局部的方法
2019/09/25 Javascript
Vue中错误图片的处理的实现代码
2019/11/07 Javascript
jQuery实现轮播图效果
2019/11/26 jQuery
写一个Vue loading 插件
2020/11/09 Javascript
[03:24]CDEC.Y赛前采访 努力备战2016国际邀请赛中国区预选赛
2016/06/25 DOTA
django 环境变量配置过程详解
2019/08/06 Python
用Python调用win命令行提高工作效率的实例
2019/08/14 Python
详解Canvas事件绑定
2018/06/27 HTML / CSS
毕业自我鉴定范文
2013/11/06 职场文书
《火烧云》教学反思
2014/04/12 职场文书
毕业生学校推荐信范文
2014/05/21 职场文书
2014卖家双十一活动策划书
2014/09/29 职场文书
缓刑期间思想汇报范文
2014/10/10 职场文书
优秀教师个人总结
2015/02/11 职场文书
社区安置帮教工作总结2015
2015/05/20 职场文书
创业项目大全(适合在家创业的项目)
2019/08/15 职场文书
利用Python判断整数是否是回文数的3种方法总结
2021/07/07 Python