查看原文
其他

Python高级编程——装饰器Decorator超详细讲解(补充篇——嵌套装饰器)

草yang年华 机器学习与python集中营 2021-09-10

送你小心心记得关注我哦!!

进入正文

全文摘要

装饰器decorator,是python语言的重要特性,上面一章节详细讲解了python装饰器与python闭包的下篇内容,主要讲解了什么是python闭包,python闭包与装饰器之间到底有什么区别和联系。本文将介绍装饰器系列文章补充,详细介绍嵌套装饰器的各部分类容,主要包括嵌套装饰器的定义,运行过程,主要作用等内容

重新声明,python高级编程——装饰器系列文章一共分为上、中、下、补充篇,本文讲解补充篇,后续还有python高级编程特性——python描述符descriptor系列文章哦,请记得持续关注哦!文章偏长,阅读全文约30min。

上篇请参考:

全文目录

01 python装饰器的运行过程

    1.1 从一个小的例子看起

    1.2 装饰器逐句运行的断点调试

    1.3 装饰器运行的总结

02 python多层装饰器的嵌套使用

    2.1 从一个实例说起

    2.2 嵌套装饰器的逐句调试

    2.3 多层装饰器的总结

03 python多层装饰器的应用场景

04 python高级编程——描述符descriptor(预告


python装饰器详解(补充篇)


此文章为,python装饰器详解——补充篇,上一篇文章中,即详解装饰器——下篇 ,已经详细讲解什么是python闭包,闭包是很多函数是编程语言重点,包括闭包的诞生背景,闭包的定义、作用、与装饰器的关系与区别等。这篇文章作为python高级编程系列文章——多层嵌套装饰器的收尾文章,将详细介绍装饰器的运行机理,多层装饰器的运行过程。该系列文章共分为 上、中、下 、补充四篇。此为第四篇


01

python装饰器的运行过程 

01 python装饰器的运行过程1.1 从一个小的例子看起


前面虽然已经讲解了python的装饰器的本质,定义、意义、使用场景,但是对与装饰器到底一句一句是怎么运行的,还没有一个好的说明,本文将再次说明。先看一个简单的:

def A_Decorator(function):
    print('我是 外层A 添加的第一个功能')
    def wrapper(a,b):
        print('我是 内层A 添加的第一个功能')
        function(a,b)
        print('我是 内层A 添加的第二个功能')
    print('我是 外层A 添加的第二个功能')
    return wrapper

@A_Decorator
def C_function(a,b):
    print('最后的结果是:{0}'.format(a+b))

C_function(100,200)

运行结果为:

我是 外层A 添加的第一个功能

我是 外层A 添加的第二个功能

我是 内层A 添加的第一个功能

最后的结果是:300

我是 内层A 添加的第二个功能

跟我们想象的是不是有所区别,并不是如下的结果:

我是 外层A 添加的第一个功能

我是 内层A 添加的第一个功能

最后的结果是:300

我是 内层A 添加的第二个功能

我是 外层A 添加的第二个功能

01 python装饰器的运行过程1.2 装饰器的逐句调试


这是为什么呢?这要从它的本质来说起了,我们可以通过设置断点调试来查看运行过程。如下动态图:

如果你不是特别明白没关系,我们通过上面的动态图可以发现,当进入到装饰器函数A_Decorator的时候,然后代码并不是从上往下依次执行,他的执行顺序大致是这样子,分为两个阶段:

(1)进入装饰器函数,先执行完外层函数的函数体,返回一个wrapper;

(2)然后在运行被包装的函数,即执行函数C_function的时候,由于被包装的函数实际上就是返回的wrapper(参见系列文章第二篇——中篇),所以再执行函数wrapper内部代码。

01 python装饰器的运行过程1.3 装饰器运行的总结

总结:装饰器的运行需要注意以下几个问题——三次“运行跳跃”

(1)在使用@decorator定一个被装饰的函数的时候,就意味着要进入装饰器函数了,什么意思呢,这样说获取大家更加明白。

C_function=A_Decorator(C_function) 这句话与@定义的是等价的,这一句话运行结束,也就意味着外层装饰器中的代码运行完了,返回了一个wrapper对象。从被装饰函数的定义调到外层装饰器的函数体,这称之为第一次跳跃。

(2)然后运行C_function,实际上也就是运行返回的wrapper,会跳入到wrapper内部执行。运行被装饰的函数,跳入到内层的wrapper执行,这称之为第二次跳跃。

(3)在wrapper内部的时候,因为wrapper内部的function实际上就是定义的函数,故而在运行wrapper内部的function函数的时候,又会重新跳跃到被装饰函数的函数体,这称之为第三次跳跃。

(4)当被装饰的函数执行完了,然后再继续回到wrapper内部执行function后面未执行完的部分。


其实归纳起来,就一句话:装饰器在包装函数的时候运行外层,在运行被包装函数的时候才运行内层wrapper,我们脑子里需要建立这样的一种思维方式才行。

     有了上面的基础之后,现在我们就可以讨论多个装饰器的嵌套了。


02

python多层装饰器的嵌套 

02 python多层装饰器的嵌套2.1 从一个简单的实例说起


所谓的装饰器的嵌套,就是使用了多个装饰器,比如A装饰了B,B又装饰了C,即相当于A给B添加了额外功能,B又给C添加了额外功能。那么现在C相当于有两个装饰器,简单的实现代码如下所示:

def A_Decorator(function):
    print('我是外层A添加的第一个功能')
    def wrapper(a,b):
        print('我是内层A添加的第一个功能')
        result=function(a,b)
        print('我是内层A添加的第二个功能')
        return result
    print('我是外层A添加的第二个功能')
    return wrapper

def B_Decorator(function):
    print('我是外层B添加的第一个功能')
    def wrapper(a,b):
        print('我是内层B添加的第一个功能')
        result=function(a,b)
        print('我是内层B添加的第二个功能')
        return result
    print('我是外层B添加的第二个功能')
    return wrapper

@A_Decorator
@B_Decorator
def C_function(a,b):
    return a+b

result=C_function(100,200)
print('--------------------------------------')
print(result)

运行结果为:

我是外层B添加的第一个功能

我是外层B添加的第二个功能

我是外层A添加的第一个功能

我是外层A添加的第二个功能

我是内层A添加的第一个功能

我是内层B添加的第一个功能

我是内层B添加的第二个功能

我是内层A添加的第二个功能

--------------------------------------

300

02 python多层装饰器的嵌套2.2 多层装饰器的逐句调试


为什么不是先把A装饰器的运行完或者是先把B装饰器的运行完,而是呈现这种交叉的形式,这需要根据“装饰器”的本质一步一步去分析了。我们同样可以设置断点调试,一步一步追踪看到底程序是怎么运行的,如下所示:

从上面的运行演示,我们发现,运行的顺序是分为如下几个步骤进行的:

(1)先运行B装饰器的外层代码,返回一个B装饰器里面的wrapper

(2)然后运行A装饰器的外层代码,返回一个A装饰器里面的wrapper

(3)然后运行被装饰器装饰的函数,此为C_function函数,由于C_function本质上就是A装饰器所返回的wrapper,所以,会优先执行A中的wrapper函数。但是这里并不是一次性将A中的内层函数wrapper全部执行完毕,而是又跳跃到B中的内层函数,这到底是怎么回事呢?如果理解了装饰器本质的同学相信已经有了答案。


实际上,两层装饰器的嵌套本质上等价于如下的代码:

C_function=A_Decorator(B_Decorator(C_function))

这个等价于

@A_Decorator

@B_Decorator

def C_function(a,b):

       return a+b

结合前面的“三次跳跃”的运行过程,那么现在就可以来分析多层嵌套装饰器的运行本质了。我们令

C_C_function=B_Decorator(C_function)  

# C_C_function作为中间的传递变量,容易知道实际上C_C_function就是B_Decorator装饰器所返回的那个wrapper,

则上面的式子等价于下面

C_function=A_Decorator(C_C_function)

则上面的代码完全等价于下面:

def A_Decorator(function):
    print('我是外层A添加的第一个功能')
    def wrapper(a,b):
        print('我是内层A添加的第一个功能')
        result=function(a,b)
        print('我是内层A添加的第二个功能')
        return result
    print('我是外层A添加的第二个功能')
    return wrapper

def B_Decorator(function):
    print('我是外层B添加的第一个功能')
    def wrapper(a,b):
        print('我是内层B添加的第一个功能')
        result=function(a,b)
        print('我是内层B添加的第二个功能')
        return result
    print('我是外层B添加的第二个功能')
    return wrapper

# @A_Decorator
# @B_Decorator
def C_function(a,b):
    return a+b

#C_function=A_Decorator(B_Decorator(C_function))
C_C_function=B_Decorator(C_function)
C_function=A_Decorator(C_C_function)

result=C_function(100,200)
print('--------------------------------------')
print(result)

现在我们依然设置断点进行调试,大家注意看下面所打印出来的信息,这有助于我们了解它到底执行那一步去了。

02 python多层装饰器的嵌套2.3 多层装饰器嵌套的总结


现在来逐句分析它的运动过程:

(1)先执行C_C_function=B_Decorator(C_function)。进入B的外层,返回一个B_wrapper,这里的C_C_function就是B_wrapper。——包装执行外层哦,还记得前面的吗?

(2)再执行C_function=A_Decorator(C_C_function)。进入A的外层,返回一个A_wrapper,这里的C_function就是A_wrapper。

(3)再执行C_function(100,200)。因为C_function就是A_wrapper,所以先进入A_wrapper;然后遇到function(a,b)的时候,因为这里的function参数实际上是通过C_function=A_Decorator(C_C_function)传递进去的,所以,function=C_C_function=B_wrapper,所以会调到B_wrapper。

(4)再执行B_wrapper的时候执行到function(a,b)的时候,这里的function实际上是C_C_function=B_Decorator(C_function)传递进去的,也就是说这里的function就是原生的C_function函数定义,也就是会调到C_function函数体里面。

(5)等到C_function函数体执行完毕,再先结束B_wrapper,然后再结束A_wrapper。


怎么样,现在明白多层装饰器的运行原理以及运行过程了吗?

本节的内容如果没有理解透彻,不能算得上是真正意义上理解了python装饰器哦,希望结合我前面的系列文章(上、中、下 三篇)结合起来多看几遍哦!


03

python多层装饰器的应用场景 

03 python多层装饰器的应用场景3.1 多层装饰器的应用场景


经过了漫长的理解,终于明白了多层装饰器原来是这么一回事啊,多层装饰器有着广泛的应用,虽然我们在使用的时候,因为python做了语法糖层面上得起封装,我们并不需要逐句分析装饰器到底是怎么运行的,但是一定要在理解的基础之上加以运用,这样才能知其然,并知其所以然哦!

     我们说了装饰器的作用就是添加额外信息,辅助信息,额外验证等等。如果你的产品经理让你写一个登录程序与权限验证程序,

    这里有两个功能,一个是要先验证登录的信息,看能不能够登录,第二个是要验证登陆的这个用户有哪些权限,这就可以通过多层装饰起来完成。具体的代码这里就不再写出来了,有兴趣的小伙伴可以自己尝试一下。

    装饰器系列文章终于结束了,区区一个Python装饰器,写了四大篇文章,自问这是最具体、最详细、最容易懂得文章了,如果对你有帮助,可以持续关注哦!


下一篇预告:

下面篇文章的重点是描述符descriptor:

描述符这个词可能对于python初学者或者是刚接触Python不久的小伙伴们比较陌生,但是他是python高级编程内容的重要原理,也是很多python高级特性的基础,鉴于描述符内容较多,我将依然采取系列文章的写法,从它的诞生背景到与原理讲解,再到代码案例这样层层递进的方式进行讲解。初步打算,描述符系列文章分为上、中、下三篇进行讲解

有兴趣的小伙伴持续关注哦!

····

电子书籍分享

下载链接:

链接:https://pan.baidu.com/s/1yAAQ1kgqJfCOys0lmLDp2Q 

提取码:drii 


····

推 荐 阅 读

Python高级编程——装饰器Decorator超详细讲解(下篇之python闭包详解)
Python高级编程——装饰器Decorator超详细讲解(中篇)
Python高级编程——装饰器Decorator超详细讲解(上篇)


2018/12/05

Wednesday

小伙伴们,如果你耐心的看完了关于python装饰器的系列文章,是不是有所收获呢?后面还会有讲解python高级编程特性的系列文章哦,欢迎小伙伴们继续关注和支持。如果你有需要,就添加我的公众号哦,里面分享有海量资源,包含各类数据、教程等,后面会有更多面经、资料、数据集等各类干货等着大家哦,重要的是全都是免费、无套路分享,有兴趣的小伙伴请持续关注!










: . Video Mini Program Like ,轻点两下取消赞 Wow ,轻点两下取消在看

您可能也对以下帖子感兴趣

文章有问题?点此查看未经处理的缓存