day 15 装饰器

时间:2019-05-11 09:41来源:计算机教程
(希望在看此篇随笔前能了解" 闭包 "这个概念,因为"装饰器"就是基于"闭包"实现的) 装饰器开闭原则: 对功能的扩展开放对代码的修改是封闭的在目标函数前和后插入一段新的代码.不改变

(希望在看此篇随笔前能了解"闭包"这个概念,因为"装饰器"就是基于"闭包"实现的)

装饰器开闭原则: 对功能的扩展开放 对代码的修改是封闭的在目标函数前和后插入一段新的代码.不改变原来的代码通用装饰器写法:#存在的意义:在不破坏原有函数调用的基础上,给韩式添加新的功能def wrapper: #fn是目标函数def inner(*args, **kwargs): # 聚合,为了目标函数的传参 ''''在目标函数之前....''''ret = fn(*arg, **kwargs) # 打散,调用目标函数,ret是目标韩式的返回值 ''''在目标函数之后....'''return ret #把目标函数返回值返回.保证函数正常的结束return inner@wrapper ### func = wrapper == @wrapperdef func():pass#### func =wrapper #此时fn就是funcfunc() #此时执行的是inner举例造人def wrapper:def inner():printfn()printreturn innerdef zaoren():printprintprintzaoren = wrapperzaoren()同一个函数被多个装饰器装饰@wrapper1@wrapper2@wrapper3def func():pass123func 321def wrapper1:def inner(*args, **kwargs):print("1111111")ret = fn(*args, **kwargs)print("2222222")return retreturn innerdef wrapper2:def inner(*args, **kwargs):print("3333333")ret = fn(*args, **kwargs)print("44444444")return retreturn innerdef wrapper3:def inner(*args, **kwargs):printret = fn(*args, **kwargs)printreturn retreturn inner# 就近原则@wrapper1@wrapper2@wrapper3def func():print("我是可怜的func")func()# 1 2 3func 3 2 1##11111113333333555555我是可怜的func666666444444442222222带参数的装饰器def wrapper_out:def wrapper:def inner(*args, **kwargs): # 聚合在目标函数之前ret = fn(*arg, **kwargs) # 打散在目标函数之后return retreturn innerreturn wrapper@wrapper_out# 执行的时候. 先执行函数的调用然后使用返回值和前面的@组合成装饰器语法糖def func():passeval=>文件操作装饰器函数开始举例def wrapper:def inner(*args, **kwargs):print('问问金老板,行情怎么样啊')ret = fn(*args, **kwargs)print('金老板骗我,恨你')return retreturn innerdef yue():printyue = wrapperyue()进行判断def wrapper_out: # 装饰器本身的参数def wrapper: # 目标函数def inner(*args, **kwargs): # 目标函数执行需要的参数if flag == True:print("问问金老板. 行情怎么样啊")ret = fn(*args, **kwargs) # 在执行目标函数之前print("金老板骗我. 恨你")return retelse:ret = fn(*args, **kwargs)# 在执行目标函数之前return retreturn innerreturn wrapper# 语法糖 @装饰器@wrapper_out # 先执行wrapper_out 返回一个装饰器再和@拼接@装饰器def yue(): # 被 wrapper装饰print("走啊. 约不?")yue()

现有以下场景:

公司里,测试团队反馈你们开发写的app用起来反应太慢了,老板给你下了个命令,需要你检查一下所有函数的执行效率,看看是否因为某些函数导致的"反应慢"

急老板之所急,你很快响应了老板的命令.

分解一下,什么是某函数的执行效率呢?拿程序员的入门函数举例:

1 def func():
2     print("Hello World!")
3     print("这是我的第一个python函数!")

在这个函数在被调用前,来个计时开始,在函数执行完之后来个计时结束,前后时间差就是这个函数的执行时间,也就是所谓的执行效率.

所以,这是要给这个func()添加一个新功能,叫"输出执行效率".

怎么做呢?

要添加功能,那么当然是改编它了,然后就有了下面的改编方式:

 1 import time
 2 def func():
 3     starttime = time.time()
 4     print("Hello World!")    # 原函数语句
 5     print("这是我的第一个python函数!")    # 原函数语句    
 6     time.sleep(1)    # 因为这个入门函数执行速度太快,起始时间差太短,所以手动给它加上1秒睡眠时间以方便显示
 7     endtime = time.time()
 8     print("func()函数的执行效率为%s秒" % (endtime - starttime))
 9 
10 func()    # 运行一次就可以打印它的效率了

结果:图片 1,成功了!......但是......

此时内心毫无波动,因为你知道,这只是对一个函数修改就这么麻烦,项目里那么多函数呢...

"一个个去改?不可能的,我这辈子都不可能一个个去改的!"就是你此时的内心独白.

你想到了"捷径":可以把添加的新代码单独拿出来,包装成另外一个函数timer(),它接收其他函数名为参数:

1 import time
2 def timer(f):
3     starttime = time.time()
4     f()
5     endtime = time.time()
6     print("%s函数的执行效率为%s秒" % (f.__name__, (endtime - starttime)))
7 
8 timer(func)    # 测试func()的执行效率
# timer(func1)  # 测试func1()的执行效率

现在快被自己聪明哭了,分分钟替领导排忧解难,势必会得到领导的夸赞...

但是...瞬间一个冷颤: 我要知道func()函数的执行效率,就需要改变它原来的执行方式func()为timer(func)了,这样来说,要在项目运行中知道某个函数的执行效率,我也要这样修改它的执行方式了啊,这得改多少地方???万万不可...

怎么办?!?!?!改,就得改所有代码,那会累死.不改,那老板会把我neng死...怎么办?!?!?!

苦思冥想中,突然想到,如果我重新命名一下我的timer(),让它伪装成func(),这样一来,在func()原先被调用的地方,现在还是func()被调用啊!

# 以下代码为了区分变量和函数,使用不同颜色标记出长得一样的两个量
1 new_func = func    # 将函数名func赋值给变量"new_func",那么new_func现在就是个函数了
2 func = timer    #将函数名timer复制给变量"func".
'''
注意此处的func变量与上面的函数名func除了长得一样外没有任何关系,但是必须长得一样,因为这是个"伪装"
回过头来看,因为python程序是由上至下按行执行的,所以后面的项目代码在调用func()的时候,实质上就是调用了func,也就是timer()
而timer()在调用的时候需要传参,把谁传进去呢?当然是把需要执行的func传进去,此时func已经赋值给了new_func,所以应该写成timer(new_func)
最后就是func(new_func)也就是timer(func),相当于使用timer()把func()的效率测试了一遍
'''

那么问题来了,原项目在调用func()时不会给它传参,只是单纯地调用它而已,而且还加了两句话...怎么办???

继续修改,在timer()函数里内嵌一个函数,外层函数的返回值设为内层函数的函数名:

1 import time
2 def timer(f):
3     def inner():
4         starttime = time.time()
5         f()
6         endtime = time.time()
7         print("%s函数的执行效率为%s秒" % (f.__name__, (endtime - starttime)))
8     return inner
# 接下来,把timer(func)赋值给func,再执行func().原理同上,一定要捋清楚谁是谁!
func = timer(func)  # func = inner
func()  # 执行func()也就等同于执行inner(),即完成了"测试func()的执行效率",同时也没有改变原项目对func()的调用
# (也就是看起来一样,实际不一样,这是个真正的"伪装"),而且只加了一句话func = timer(func)做了一个关系转换.

这样就完成了一个"最简单版"的装饰器(姑且称之为timer v1.0).到此,已完成所需的功能.

但是中的但是,python语法三大特点: 简洁,优美,清晰.它觉得你加的这句func = timer(func)还是不够简洁优美清晰,怎么办???

其他编程语言都提供了一个东西,叫"语法糖"(语法糖指那些实质上没有给计算机语言添加新功能,而只是对人类来说更“甜蜜”的语法.语法糖往往给程序员提供了更实用的编码方式,有益于更好的编码风格,更易读).那么python也有语法糖,在装饰器这里就>>>用"@"符号,接装饰器的函数名<<<置于需要被装饰的函数前面,如:

1 def timer():
2 ...    # 为节省空间,此处就不详述timer()的构造
3 @timer    # 意思就是func = timer(func)
4 def func():
5     print("Hello World!")
6     print("这是我的第一个python函数!")

至此,完成了装饰器v1.0的升级(此时应该叫timer v2.0了).

(也许有疑问,如果项目中有1000个函数,那还是需要加1000个@timer啊?....对!但是,需求不应该是现在才提出来,而是在项目代码构建之初就有,所以在项目代码编写的时候,顺手一个@timer就完成了啰啰嗦嗦的好几句话,是不是方便很多了!)

装饰器一般用来干嘛: 给函数增加"打印日志","测试效率","登录认证"等等额外功能的同时,还可以不改变原函数的调用方式

 

还没做完.

 

编辑:计算机教程 本文来源:day 15 装饰器

关键词: