洗礼灵魂,修炼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))

结果:

小红看后拍手叫好,继续研究这个带参数的代码,小红确实有程序员的素质,遇到问题爱思考,小红又想如果直接返回原函数呢?不调用看看:

 果然如此啊,小红越发钦佩老大,觉得今晚来老大家里来对了,然后………………(未完待续,此处省略两万个字 /滑稽)

好的,我借用经典案列修改的的例子已经结束。是的,没错案例中老大用的就是—装饰器,相信看完这个例子,你已经对装饰器有了很不错的理解了。

 

下一篇讲解装饰器进阶篇

由于有朋友跟我提意见说,一篇博文太长看得累,所以以后如果有知识点太多的我尽量分成两份或者三份写

原文地址:https://www.cnblogs.com/Eeyhan/p/7658033.html