浅显易懂的谈一谈python中的装饰器!!
hello大家好~~我是稀里糊涂林老冷,一天天稀里糊涂的。
前一段时间学习了装饰器,觉着这东西好高大上哇靠!!哈哈,一定要总结一下,方便以后自己查阅,也希望帮助其他伙伴们共同进步!
装饰器:
大家可以这样理解,装饰器是运用闭包的基本原理,对一个目标函数进行装饰。即是在执行一个目标函数之前、之后执行一些特定的事情。
学习装饰器一定要有闭包的基础知识,如果对闭包不是特别理解的话,可以参考我之前的博文http://www.cnblogs.com/Lin-Yi/p/7305364.html,也可以学习其他大神的博文哦~~~一定要弄明白闭包,要不然装饰器很难理解的。
不理解的话举个例子:
工作中,他写了登录功能,我写了注销功能,他写了保存个人信息功能.........你和你的伙伴是一个团队,你们完成各种各样的功能。但是再软件上线的时候呢,技术总监说了,我们要统计每一个功能模块执行耗费的时间,针对耗费时间太多的功能,可能我们要开会商讨进行优化。
问题来了,我们团队五六个人,每个人写了两三个功能,都要统计执行实现,是不是每个人都要修改自己的代码,在功能模块函数开始的时候取一下时间,结束的时候取一下时间,然后做一个减法。每一个人的每一个功能都去增加这样一些代码,是不是可麻烦了呢!是不是可浪费时间了呢!!那怎么办?? 用装饰器!
我们只需要编写一个装饰器,用这个装饰器对我们写的所有功能进行装饰,就能达到同一个目的!!哈哈哈,很牛有木有!我们一起看看吧~
先上一段代码!!!
import time
#装饰器会用到闭包的原理:外函数内部定义了一个内函数,内函数使用外函数的局部变量,并且外函数返回了内函数的引用
#装饰器函数 传入一个我们想对他装饰的目标函数的引用,将在内函数中使用
def decorator( func ):
def inner():
t1 = time.time() # 目标函数开始之前取一下时间
print("登录前的时间是",t1)
# 这个func外部函数传入的参数,也就是我们希望装饰的目标函数的引用
# 这里实际上执行了目标函数,我们想对这个函数进行装饰,所有在它执行之前和之后进行一番操作,具体什么操作看业务逻辑
func()
t2 = time.time() # 目标函数结束之后取一下时间
print("登录后的时间是",t2)
t = t2 - t1 #计算目标函数执行花了多长时间
print("登录花费的时间:",t)
#在外函数结束时候返回内函数的引用
return inner # 上面是我们写的装饰器函数,用装饰器函数可以对一个目标函数进行操作!!
'''
下面的 @decorator 就是是使用装饰函数decorator的意思
它实际上执行的是: login = decorator( login )
注意,编程语言都是从右向左来解析执行,这句话实际上会发生的事情是:
1 把目标函数login(是一个变量名,里面存的是目标函数的引用) 传入decorator函数被参数func接收了
这时候func也是目标函数的引用 func和login指向同一个函数对象
2 decorator函数定义了内部函数inner,发现里面会使用到func,
这用到闭包的原理,外函数结束的时候会把func绑定给内函数将来使用
3 外函数结束的时候把自己创建的内函数的引用inner返回给了login接收,
这时候login已经不是原来我们编写的目标函数了,login实际上是一个inner函数的实例对象
再执行login() 的时候实际上执行了inner()的一个对象
4 再执行login() 的时候 实际上执行inner()
会先执行取时间 打印
之后执行func()才是执行我们的目标函数
最后又取时间 打印结果
'''
@decorator
#下面我们写一个函数模拟是登录功能模块的函数,并且用装饰器进行装饰
def login():
print("用户正在登录!请稍等。。。。")
time.sleep(1)
print("登录成功!欢迎登录!") if __name__ == '__main__':
#调用login 实际上调用了装饰之后的函数inner的一个对象,inner里的func才是真正的目标函数
login()
'''
执行结果:
登录前的时间是 1502163280.2836263
用户正在登录!请稍等。。。。
登录成功!欢迎登录!
登录后的时间是 1502163281.2895217
登录花费的时间: 1.0058953762054443
'''
不知道大家有没有看懂这段代码。认真读一读注释的话,我想有闭包基础的伙伴们一定能读懂装饰器。
总之装饰器很神奇,运用引用之间互相改变,实现了一段很厉害的功能!
但是呢,其实上面的装饰器非常不好。我写这样一个装饰器,只是为了帮助大家理解装饰器的执行过程和实现的结果。
请大家继续跟着我的思路:
我们先回顾一下:上面的目标函数login,当使用@decoration的意思是,login = decoration(login),我们把目标函数当作参数传给了decoration,然后decoration返回了一个inner给login。这时候login就已经不再是之前的login了,而是装饰函数里的inner函数的一个实例对象。当我们再执行login() 实际上执行了inner() ,原来的目标函数被装饰器的func保存了,在inner里面决定了什么时候执行func。
但是问题来了!
1 如果我们想给这个login目标函数传入参数:当调用login()的时候实际上调用的是inner(),如果我们想给login传参,但是却传参给了inner,inner又没有接收参数,这就会报错!!!
2 如果我们login函数有返回值,调用login()实际调用了inner(),inner内执行的func()才是执行的目标函数,func执行的时候又没有设置变量接收返回值,这样目标函数的返回值就丢了!!!
为了让我们的装饰器能够在各种各样的目标函数上使用,不论有没有参数,有没有返回值,我们都不会丢数据也不会报错,我们要对我们的装饰器进行修改,让它变得更加健壮!
一、通用的装饰器模板:
上代码! 我们把装饰器函数简化一下,不要他那么复杂。请看下面的装饰器实例:
#装饰器函数 传入目标函数做参数
def decorator( func ):
#实际调用目标函数会发生调用inner,
# 所以我们让inner接收不定参数,我们再把不定参数传给目标函数func
#这样不论我们传入什么参数目标函数都能接收到!!
def inner( *args , **kwargs ):
print("装饰器函数中。。目标函数执行之前的操作!!")
# 我们设置一个变量接收目标函数的返回值,在inner结束的时候再把返回值返回去
# python不同于其他语言,即使我们没有编写func的返回值,也会默认返回None,所以这里不会报错
res = func( *args , **kwargs )
print("装饰器函数中。。目标函数执行之后的操作!!")
return res
return inner
#这样编写的装饰器,在外部看来,我们就可以传入参数给目标函数
# 同时也可以正常接收目标函数返回的参数!!! @decorator #实际上会发生 destination = decorator( destination )
# 把目标函数传入装饰器函数返回了inner给destination
# 此后我们再调用destination 实际上调用了inner函数的一个对象
def destination( a ):
print( "目标函数接受到参数:%s"%a )
return "目标函数的返回值%s"%a if __name__ == '__main__':
# 这里实际上调用了inner函数的对象,我们传入参数和接收返回值都没有问题了!
res = destination(10)
# 打印一下返回值!
print(res)
'''
执行结果:
装饰器函数中。。目标函数执行之前的操作!!
目标函数接受到参数:10
装饰器函数中。。目标函数执行之后的操作!!
目标函数的返回值10
'''
以上这个装饰器,实际上就可以作为一个通用版本的装饰器来使用了,它基本可以为各种各样的函数进行装饰。
我们传入目标函数的参数 在inner中设置不定参数去接收,又传给了func
func的返回值我们设置了变量res去接,又在inner结束时候返回了res
这样从外部看来,调用destination 只是单纯的被包装,并不知道里面这么复杂。
ok啦!!到这里呢,装饰器基本就介绍完了,下面来一点拓展!
二、多层装饰器嵌套:
#多层装饰器的嵌套 #外层装饰器函数
def decorator_out(func ):
def inner( *args , **kwargs ):
print("外装饰器前置操作。。。。。。。。。")
res = func( *args , **kwargs)
print("外装饰器后置操作..............")
return res
return inner #内层装饰器函数
def decorator_in( func ):
def inner( *args , **kwargs ):
print("内装饰器前置操作。。。。。。。。。")
res = func( *args , **kwargs)
print("内装饰器后置操作..............")
return res
return inner #目标函数被两个装饰器装饰
'''
这里实际上会发生的情况是 login = decorator_out( decorator_in( destination ) )
先in装饰器装饰目标函数之后,把inner返回给destination,
然后 out装饰器 再对新的destination进行装饰,out里的func 存了 in装饰器装饰过的destination函数
又把inner返回给destination 这时候再执行目标函数destination() 实际上
1 执行out装饰器的inner() 执行到func的时候 执行in装饰器的inner
2 在内装饰器inner中执行func才是原来的目标函数
3 目标函数执行完跳出到in装饰器的inner
4 in装饰器函数inner执行完 相当于out装饰器的func执行完 跳到out装饰器的inner中
5 out装饰器执行结束,全部过程结束 '''
@decorator_out
@decorator_in
def destination():
print("目标函数!") if __name__ == '__main__':
destination()
'''
执行结果:
外装饰器前置操作。。。。。。。。。
内装饰器前置操作。。。。。。。。。
目标函数!
内装饰器后置操作..............
外装饰器后置操作..............
'''
当多层装饰器嵌套的时候,实际上先内层装饰器装饰目标函数
外层装饰器会对内层装饰器装饰的结果进行再装饰
不太好理解呀!!我很害怕好伙伴们理解不了我想表达的事情。如果是在不理解,不妨敲一敲代码,看一看注释,也许就懂了!!我也是懵懵懂懂学过来的~
三、可选择装饰器:
接下来我们再对装饰器来一个功能!
团队中一个人写了装饰器,大家一块用,之前的代码中,我写的装饰器,都是对目标函数执行之前和之后都有一些操作。如果某一个功能不需要之前或者之后的操作,只需要一个操作怎么办??
这时候用到三层闭包嵌套。我们来一段代码理解一下
#我们传入一个flag决定是不是要执行目标函数之后的操作
def flagOperation( flag ):
def decorator(func):
def inner(*args , **kwargs):
print("装饰器前置操作。。。。。。。。")
res = func(*args , **kwargs)
if flag : #如果传入flag是真则执行后置操作 否则不执行
print("我是后置操作")
return res
return inner
return decorator '''
@flagOperation(True) 这里实际上先执行了flagOperation(True) ,返回了decorator装饰器,
并且把flag的值是True绑定给了他
这时候用带着flag的decorator 对目标函数进行装饰
相当于@decorator 带着一个flag
'''
@flagOperation(True)
def destination():
print("目标函数!") @flagOperation(False)
def desti2():
print("目标函数2") if __name__ == '__main__':
destination()
'''
结果:
装饰器前置操作。。。。。。。。
目标函数!
我是后置操作
'''
desti2()
'''
结果:
装饰器前置操作。。。。。。。。
目标函数2
'''
ok啦,到目前位置,已经探讨了好多种牛逼的装饰器了,他们功能非常强大。学习要站在巨人的肩膀上,我么能完成更加厉害的功能。
对于理解有困难的好伙伴,只要努力的把我分享的 通用的装饰就可以了 其他可以不用理解。
四、类实现装饰器
如果对一个类名后跟() , 实际上会调用类内的__call__方法,所以我们把装饰器的逻辑写到__call__ 方法里就可以!
#类装配期
class Decorator(object):
def __init__(self,func):
self.func = func
def __call__(self):
print("类装饰器前操作")
self.func()
print("类装饰器后操作") @Decorator
def login():
print("login now") if __name__ == '__main__':
login()
不装饰器到这里还有一些小问题没有解决。
目标函数是一个函数对象,它里面有自己的魔法属性 函数名__name__ 、说明文档__doc__等等。
如果我们目标函数被装饰器装饰之后,我们再调用 目标函数 destination.__name__ 会发现 本来 我们本来想要的 destination这个名字 打印出来确是 inner
这种情况确实不出乎意料,因为destination被装饰之后确实存的就是inner了,我们对destination的操作实际上都是对inner的操作
但是这带来了使用的不一致性。我们如果能把本来目标函数各种方法覆盖给inner函数就好了!
当然这是能实现的,我们可以用functools 包里的工具来实现。这里就不探讨了,再后续的博文中,我会再发表我学习functool模块的一些常用工具!
希望大家有所收获,我们共同进步!
欢迎其他朋友批评指正!
浅显易懂的谈一谈python中的装饰器!!的更多相关文章
- 简单说明Python中的装饰器的用法
简单说明Python中的装饰器的用法 这篇文章主要简单说明了Python中的装饰器的用法,装饰器在Python的进阶学习中非常重要,示例代码基于Python2.x,需要的朋友可以参考下 装饰器对与 ...
- 【Python】python中的装饰器——@
对装饰器本来就一知半解的,今天终于弄清楚了,Python中的装饰器是对装饰者模式的很好运用,简化到骨子里了. python中为什么需要装饰器,看这里:http://www.cnblogs.com/hu ...
- Python 中实现装饰器时使用 @functools.wraps 的理由
Python 中使用装饰器对在运行期对函数进行一些外部功能的扩展.但是在使用过程中,由于装饰器的加入导致解释器认为函数本身发生了改变,在某些情况下——比如测试时——会导致一些问题.Python 通过 ...
- 写python中的装饰器
python中的装饰器主要用于在已有函数实现功能前附加需要输出的信息,下面将用实例展示我如何写装饰器. 首先分别尝试写装饰器装饰一个无参函数和一个有参函数(被装饰函数仅输出,无返回值情况下) def ...
- python中的装饰器decorator
python中的装饰器 装饰器是为了解决以下描述的问题而产生的方法 我们在已有的函数代码的基础上,想要动态的为这个函数增加功能而又不改变原函数的代码 例如有三个函数: def f1(x): retur ...
- python中@property装饰器的使用
目录 python中@property装饰器的使用 1.引出问题 2.初步改善 3.使用@property 4.解析@property 5.总结 python中@property装饰器的使用 1.引出 ...
- 【Python】解析Python中的装饰器
python中的函数也是对象,函数可以被当作变量传递. 装饰器在python中功能非常强大,装饰器允许对原有函数行为进行扩展,而不用硬编码的方式,它提供了一种面向切面的访问方式. 装饰器 一个普通的装 ...
- 三分钟搞定Python中的装饰器
python的装饰器是python的特色高级功能之一,言简意赅得说,其作用是在不改变其原有函数和类的定义的基础上,给他们增添新的功能. 装饰器存在的意义是什么呢?我们知道,在python中函数可以调用 ...
- python cookbook 学习系列(一) python中的装饰器
简介 装饰器本质上是一个Python函数,它可以让其他函数在不需要做任何代码变动的前提下增加额外功能,装饰器的返回值也是一个函数对象.它经常用于有切面需求的场景,比如:插入日志.性能测试.事务处理.缓 ...
随机推荐
- Unity3D UGUI强制刷新Layout(布局)组件
UGUI的Layout布局组件确实节省了我们很多代码 如果不使用Layout组件 那么光在计算UI的布局上就要花费很大的功夫 特别是动态生成其组件的时候 当然,Layout组件在大多数时候是非常好用的 ...
- IE浏览器清除缓存没用
再想买更新JS和css文件之后, 使用 internet 里面的删除选项 发现样式和事件还是没用变 最终发现 需要 按 f12 找到这个清缓存才正常解决问题
- 边做边学入门微信小程序之仿豆瓣评分
微信小程序由于适用性强.逻辑简要.开发迅速的特性,叠加具有海量活跃用户的腾讯公司背景,逐渐成为了轻量级单一功能应用场景的较佳承载方式,诸如电影购票.外卖点餐.移动商城.生活服务等场景服务提供商迅速切入 ...
- C++string类总结
一.string的初始化 首先,为了在程序中使用string类型,必须包含头文件 <string>.如下: #include <string> 注意这里不是string.h,s ...
- Matlab绘图基础——一些标准三维曲面
标准三维曲面 t=0:pi/20:2*pi; [x,y,z]= cylinder(2+sin(t),30); %[x,y,z]= cylinder(R,n),其中R为圆周半径,n为组成圆周的点 ...
- i/10和i取最后两位的精妙算法(前方高能)
i/10; q2 = (i2 * 52429) >>> (16+3); 52429/524288 = 0.10000038146972656, 524288 = 1 << ...
- 涉及模式之 装饰器模式详解(与IO不解的情缘)
作者:zuoxiaolong8810(左潇龙),转载请注明出处,特别说明:本博文来自博主原博客,为保证新博客中博文的完整性,特复制到此留存,如需转载请注明新博客地址即可. LZ到目前已经写了九个设计模 ...
- 【动态规划】记忆搜索(C++)
前几天还在踟蹰我应该注重培养做项目的能力还是修炼算法以及数据结构,然后发现这个场景有点似曾相识.曾几何时的一个月里,我有三件比较重要的事情要解决,在那个月刚开始的时候我一直在想我应该从那件事情开始着手 ...
- 【WCF系列】(二)设计和实现服务协定
设计和实现服务协定 WCF术语介绍 服务(Service):服务是一个构造,它公开一个或多个终结点,其中每个终结点都公开一个或多个服务操作. 终结点(EndPoint):终结点是用来发送或接收消息(或 ...
- alpha冲刺第四天
一.合照 二.项目燃尽图 三.项目进展 今天实现了登录界面和服务器的连接了,牵手成功. 一些具体的界面细化实现,一些button的响应实现 四.明日规划 登录界面和服务器的连接实现耗费了太多时间,接下 ...