洗礼灵魂,修炼python(29)--装饰器(1)—>利用经典案例解析装饰器概念
前提必备
不急着进入正题,在前面函数作用域那一章介绍了闭包,全局变量局部变量,这里再看几个简单的闭包案例:
1):不带参数
注意:
1.这里的name属性是每个函数都有的,可以反馈函数名
2.temp()是调用函数的意思,如果直接temp只是一个函数对象,并且打印出这个函数的内存地址
2):外层函数带参数
3):内外层函数都带参数
如果希望让内层函数修改外层函数的参数并返回呢?前面说过的,这样会引起异常,为什么会引起异常以及怎么解决在函数作用域那一章说过的,不记得的自己回头看下。在这里直接略过,本篇文章不需要此知识点。
能掌握以上的与函数有关的代码原理,才能方便进入下面的正题。
那么本篇博文的主旨是装饰器,什么是装饰器,怎么用装饰器,装饰器能干什么?这些问题通过下面的经典例子都可以得到解答
案例
首先说明,我是借用了其经典例子但没有全部照搬,经典例子的原始出处我已经找不到,因为网上到处都能找到类似的,找到一个发了原帖位置的,点进去网页报错404。如果侵权,请原作者联系我及时删除
案例:有一个大公司,各个基础平台部负责内部应用程序及API的开发,有上百个业务部门负责不同的业务,他们各自调用基础平台部提供的不同函数处理自己的业务
如下:
def bumen1(): print("部门1数据......") def bumen2(): print("部门2数据......") def bumen3(): print("部门3数据......") ………………………… def bumen100(): print("部门100数据......") #各部门分别调用: bumen1() bumen2() bumen3() bumen100() #结果: 部门1数据...... 部门2数据...... 部门3数据...... …………………… 部门100数据......
问题来了,由于公司目前正处在创业进步期,但是当初基础平台部开发这些函数时,因为各种原因,并没有为函数调用进行安全认证。现在,技术总部老大决定立即弥补这个缺陷。为了解决这个问题,老大找了公司里几个员工:
1)老大叫来了刚入职的技术部小李,小李的方案是:跑上跑下逐个和部门交流沟通,让他们自己在代码里加上认证功能。
然而,当天小李被开除了。
2)老大又从负责运维的小组组长叫来了小王,小王的方案是:写个复杂的shell脚本,勉强实现了功能。
老大还是不太满意,让小王下去忙去了。
3)老大又叫来了技术部老手小张,小张的方案是:只对基础平台的代码进行重构,让各个业务部门无需做任何修改:
def authentication(): #认证函数 if True: #(判断代码块,作伪代码意思一下,因为公司有哪些部门怎么认证没有已知条件具体不好说,没有展开具体的判断语句) print("认证成功") return 1 while authentication(): def bumen1(): print("部门1数据......") def bumen2(): print("部门2数据......") def bumen3(): print("部门3数据......") ………………………… def bumen100(): print("部门100数据......") else: print("认证失败")
老大看了眼睛里露出一丝喜悦,然后拍拍小张的肩膀,让小张忙去了
4)老大又叫来公司里的唯一一个女程序员小红,小红的方案是:其他不变,在每个部门加入一个认证函数:
def authentication(): print("认证成功!") def bumen1(): login() print("部门1数据......") def bumen2(): login() print("部门2数据......") def bumen3(): login() print("部门3数据......")
………………………………
def bumen100(): login() print("部门100数据......")
老大看完邪魅一笑,看着小红可爱的脸蛋,慢慢靠近小红,小红开始心跳加速,小脸微红,略显紧张,但还是没有立即起身起来,等着老大的发号施令,老大站定,说:你的代码大体没问题,功能也实现了,但是写代码要遵循开放封闭原则,虽然这个原则主要是针对面向对象开发,但是也适用于很多实际的开发中。开放封闭原则规定已经实现的功能代码内部不允许被修改,但外部可以被扩展,即封闭已实现的功能代码块,开放对扩展开放。如果将开放封闭原则应用在上述需求中,那么就不允许在函数bumen1,bumen2,bumen3……bumen100的内部进行代码修改的。
小红恍然大悟并若有所思,过了一会儿,小红对老大说:“还是大哥厉害,可以教教我怎么改吗?”,老大爽快的答应了,嘴角向上一扬说:“今晚去我家详解介绍吧”。小红抿嘴一笑微微点头同意了。
晚上在老大家里,老大写下这段参考代码:
#/usr/bin/env python #coding:utf-8 def outer(func): def inner(): print("认证成功!") return func() return inner @outer def bumen1(): print("部门1数据......") @outer def bumen2(): print("业务部门2数据......") @outer def bumen3(): print("部门3数据......") @outer def bumen100(): print("部门100数据......") #各部门分别调用 bumen1() bumen2() bumen3() bumen100() #结果: 认证成功! 部门1数据..... 认证成功! 部门2数据..... 认证成功! 部门3数据..... 认证成功! 部门100数据.....
小红看了下,有些不解道:为什么要用两层函数?一层不行吗?小红写下只有一层的代码:
#/usr/bin/env python #coding:utf-8 def outer(func): print("认证成功!") return func() @outer def bumen1(): print("部门1数据......") @outer def bumen2(): print("业务部门2数据......") @outer def bumen3(): print("部门3数据......") @outer def bumen100(): print("部门100数据......") #各部门分别调用 bumen1() bumen2() bumen3() bumen100()
结果:
发现结果是运行了,但是报错了,小红问老大什么情况,老大啥都没说,把代码删除了一点,让小红运行看:
#/usr/bin/env python #coding:utf-8 def outer(func): print("认证成功!") return func() @outer def bumen1(): print("部门1数据......") @outer def bumen2(): print("业务部门2数据......") @outer def bumen3(): print("部门3数据......") @outer def bumen100(): print("部门100数据......")
结果:
小红发现,各个部门的函数还没调用呢就已经运行了,小红心想,这样如果运用于实际开发要出事啊,各个部门都还没开工,我一个代码就直接帮他们搞定了,没搞错还好,万一搞错了,我岂不背大锅啊?这绝对不行。小红又把老大给的代码删除了调用函数的那几行试了下:
发现利用老大的代码删除了那几行调用函数的代码,啥都不会运行,这才符合逻辑的,小红开始真正的佩服老大。小红继续研究代码,过了一会儿,她想,外层函数可以认证吗?试试看:
果然又是,各个部门的函数还没调用就开始有反馈信息了,这肯定是不行,老大在一旁看了下,说道:“你这个想法不错。其实代码中如果有装饰器的话,代码会优先运行装饰器函数,所以当代码运行时,装饰器已经蓄势待发等着被装饰的函数调用并装饰它了,同时,如果有多个装饰器,装饰器之间优先级由上而下”
小红总结道:程序默认是由上而下运行,而在程序中如果有装饰器,装饰器的优先级比一般定义的函数的优先级高,同时,如果有多个装饰器,默认装饰器之间优先级由上而下
小红继续看代码,突然又想到一个问题,这样的代码,虽然暂时没问题,但是如果不在认证函数那里调用函数只是返回一下原函数对象,让原函数自己调用呢?因为实际开发中,不可能只是作为一个print就完事了的
小红把装饰函数返回函数的括号去掉了,结果:
这个结果有点小红出乎意料,发现各个部门的函数不工作了,小红试着print一下部门1的函数
原来被装饰函数在调用时因为被装饰后成了一个函数对象,没有调用了。结果:(只截取了部分,代码没变)
加上调用后:(代码一样,只截取结果)
但这样,岂不是每个部门调用时都要还要多加括号来调用一次?小红不解了,把这个情况告诉刚洗完澡出来的老大,老大看了下,想了下说:“之前我给的代码只是简单的测试,如果各个部门不传参数,是没问题的,如果各个部门要传入参数,确实是需要你这样改代码,不过,原理也一样,直接在装饰函数那里改下就行了":
#/usr/bin/env python #coding:utf-8 def authentication(func): def inner(args): print("认证成功!") return func(args) return inner @authentication def bumen1(num): print("部门1数据......") return '部门'+str(num) @authentication def bumen2(num): print("部门2数据......") return '部门'+str(num) @authentication def bumen3(num): print("部门3数据......") return '部门'+str(num) @authentication def bumen100(num): print("部门100数据......") return '部门'+str(num) #各部门分别调用 print(bumen1(1)) print(bumen2(2)) print(bumen3(3)) print(bumen100(100))
结果:
小红看后拍手叫好,继续研究这个带参数的代码,小红确实有程序员的素质,遇到问题爱思考,小红又想如果直接返回原函数呢?不调用看看:
果然如此啊,小红越发钦佩老大,觉得今晚来老大家里来对了,然后………………(未完待续,此处省略两万个字 /滑稽)
好的,我借用经典案列修改的的例子已经结束。是的,没错案例中老大用的就是—装饰器,相信看完这个例子,你已经对装饰器有了很不错的理解了。