洗礼灵魂,修炼python(29)--装饰器(1)—>利用经典案例解析装饰器概念
2018-03-07 21:09
507 查看
前提必备
不急着进入正题,在前面函数作用域那一章介绍了闭包,全局变量局部变量,这里再看几个简单的闭包案例: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("认证成功! cb0a ") 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))结果:
小红看后拍手叫好,继续研究这个带参数的代码,小红确实有程序员的素质,遇到问题爱思考,小红又想如果直接返回原函数呢?不调用看看:
果然如此啊,小红越发钦佩老大,觉得今晚来老大家里来对了,然后………………(未完待续,此处省略两万个字 /滑稽) 好的,我借用经典案列修改的的例子已经结束。是的,没错案例中老大用的就是—装饰器,相信看完这个例子,你已经对装饰器有了很不错的理解了。
下一篇讲解装饰器进阶篇
由于有朋友跟我提意见说,一篇博文太长看得累,所以以后如果有知识点太多的我尽量分成两份或者三份写
相关文章推荐
- 洗礼灵魂,修炼python(30)--装饰器(2)—>装饰器总结+进阶使用
- 洗礼灵魂,修炼python(15)--列表进阶话题—>列表解析/列表生成器
- 洗礼灵魂,修炼python(25)--自定义函数(6)—从匿名函数进阶话题讲解中解析“函数式编程”
- 洗礼灵魂,修炼python(27)--异常处理(1)—>了解异常
- 洗礼灵魂,修炼python(20)--自定义函数(1)—基础概念
- 洗礼灵魂,修炼python(23)--自定义函数(4)—闭包进阶问题—>报错UnboundLocalError: local variable 'x' referenced before assign
- 洗礼灵魂,修炼python(35)--面向对象编程(5)—多重继承,组合
- 洗礼灵魂,修炼python(24)--自定义函数(5)—匿名函数lambda
- 洗礼灵魂,修炼python(36)--面向对象编程(6)—类的相关内置函数issubclass,hasattr等
- 洗礼灵魂,修炼python(37)--面向对象编程(7)—了解魔法方法
- 洗礼灵魂,修炼python(9)--灵性的字符串
- 洗礼灵魂,修炼python(39)--面向对象编程(9)—魔法方法表
- 洗礼灵魂,修炼python(10)--有趣的判断分支+从实例中掌握循环语句
- <29>python学习笔记——装饰器
- 洗礼灵魂,修炼python(40)--面向对象编程(10)—定制魔法方法+time模块
- 洗礼灵魂,修炼python(11)--python函数,模块
- 洗礼灵魂,修炼python(26)--编程核心之“递归”
- 洗礼灵魂,修炼python(5)--python操作符,内置函数
- 洗礼灵魂,修炼python(12)--python关键词,包
- 洗礼灵魂,修炼python(34)--面向对象编程(4)—继承