简单了解Python装饰器实现原理

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/MonaLisaTearr/article/details/80661937
        

        有过开发经验得朋友队装饰模式这个词应该不陌生,装饰装饰,顾名思义就是指对我们原来有得东西进行装饰,比如我们买了新房,那么我们对毛坯房的装修,就是对我们房子进行拓展,让它更加完善!同样得对于代码也是如此,我们就是对我们原有的功能进行功能的拓展!

一、假设我们有下面的一个场景

1、假设你现在是某个公司的工程师,你当前负责得事情就是公司某个项目整个系统架构基本功能得开发,后续会有很多同事会直接调用你写得功能函数。于是你就风风火火得一顿狂撸,终于有了下面代码:
def test1():
    print("====给员工加薪了!====")
	
def test2():
    print("====出新版本,杀一个程序员祭天!====")

很好,同事们调得很舒服,系统跑得很好,老板赚钱。。。

2、某一天,老板突然来找你,说有个员工莫名奇妙被加薪了,并不是老板自己加的,老板说,加薪这里要验证下,只有我才有权限给员工加薪。又说最近程序员急剧减少,被杀得太多了,也要加权限,只有老板才能杀!

3、作为一个出色得程序员,这点要求当然没问题,于是你又一顿加班加点狂改,把老板的需求都是实现了,于是又有了下面的代码:
def test1():
    if ROLE == "boss":
    	print("====给员工加薪了!====")
    else:
        print("====不好意思,您不是老板====")
	
def test2():
    if ROLE == "boss":
        print("====出新版本,杀一个程序员祭天!====")
    else:
	print("====不好意思,您不是老板====")

4、这一切看起来很正常,但是我们不妨假设下,如果有10000个类似得功能函数需要添加类似得验证、又或者添加其它的功能呢?我们姑且假设你单身多年得手速非常快,但是这样搞法也是很浪费时间的吧?更何况贸然得去改动原来得代码,风险也是不小的,所以我们换下面这种方式看看!

二、闭包得引入

1、之前我已经分享过闭包的学习笔记了,不了解的可以去看下,闭包得几个条件如下:一个函数内定义了内函数,并且内涵是使用了外函数的局部变量,最后外函数返回了内涵的引用。满足了这三个条件我们就可以称之为闭包。


2、那么我们到底引入闭包做什么呢?闭包又是如何解决我们上面的问题得呢?我们先看下下面这段代码:

def iwapper(fun):
    inner():
        if ROLE == "boss":
            print("====身法验证通过====")
	    fun()
	else:
	    print("====不好意思,您不是老板====")
    return inner
				
def test1():
    print("====给员工加薪了!====")
	
def test2():
    print("====出新版本,杀一个程序员祭天!====")
		
		
#调用iwapper,并传入test1函数的引用,给员工加薪
addMoney = iwapper(test1)
addMoney ()

#调用iwapper,并传入test2函数的引用,杀程序员
killDev = iwapper(test2)
killDev()

打印下结果:


此时,我们产生了两个新函数,一个是addMoney(),另一个是killDev(),调用这两个即实现了添加验证得功能,保持了原来功能函数不用修改,并且大大减少了代码得冗余,但是另一个问题又来了,代码得调用方式变了,我们需要告知其他所有人,修改他们得代码!很显然这是一个更糟糕得做法!

3、路总是一步步走出来得,经历了那么多波折,真相也越来越接近了,聪明得你很快又拿出了下面得方案:

def iwapper(fun):
    inner():
        if ROLE == "boss":
            print("====身法验证通过====")
	    fun()
	else:
	    print("====不好意思,您不是老板====")
    return inner
				
def test1():
    print("====给员工加薪了!====")
	
def test2():
    print("====出新版本,杀一个程序员祭天!====")
			
#调用iwapper,并传如test1函数的引用,给员工加薪
test1 = iwapper(test1)
test ()

#调用iwapper,并传如test2函数的引用,杀程序员
test2 = iwapper(test2)
test2()

打印下结果:


        这里我们可以看到我们并没有对原有功能函数进行修改,并且提供给外部调用得方式依然是test1()、test2(),已经满足了我们得条件了,那么我们一步步的分析下:
1>首先定义了一个test1()函数的时候,就相当于再内存中创建了一块内存区域,我们姑且叫它A。同样得我们在定义iwapper(fun)的时候,在其内部定义了inner()函数,我们姑且把它所指向的内存区域命名为B。
2>紧接着,当我们调用iwapper(test1)的时候,我们把test1()的引用传了进去,所以fun此时指向了test1()的引用,也就是指向了A
3>接下来我们使用test1接收了iwapper(fun)得返回值,而iwapper(fun)返回得是inner()的引用,所以此时test1指向了B
4>所以最后我们调用test1()函数得时候其实是调用了inner(),而inner()函数除了进行校验功能之外还调用了fun()函数,从第二点我们可以知道,fun指向了A也就是最开始定义得test1(),这样我们即进行了验证功能,又对原来功能进行调用!

三、装饰器

1、说了那么多,我们来看看如果遇到上面的需求,如果使用Python装饰器来实现的话会怎么样?看下面代码:

def iwapper(fun):
    inner():
        if ROLE == "boss":
            print("====身法验证通过====")
	    fun()
	else:
	    print("====不好意思,您不是老板====")
    return inner
				
@iwapper
def test1():
    print("====给员工加薪了!====")

@iwapper
def test2():
    print("====出新版本,杀一个程序员祭天!====")

那么看下效果:


很明显,效果实现了,这时候也许就又朋友要说道说道了,nmmp,Python解析器那么简单,你前面说那么多废话干嘛,直接一开始就来这段不就行了?朋友莫激动,先把刀放下,其实如果我们单看最后面的代码,我们仅仅知道@iwapper是python装饰器的实现方式而已,但是我们并不知道这句代码到底干了什么事,而我们前面说的就是python解析器的实现原理了!其中得思想获取会对我们有所帮助也不一定呢!

总结:这里仅仅是对Python中装饰器得最简单使用方式进行学习,更重要得是学习Python决绝类似问题的思想!其更多用法大家可以查阅相关得资料,比如说带参数的装饰器,多个装饰器一起使用等等!


猜你喜欢

转载自blog.csdn.net/MonaLisaTearr/article/details/80661937