装饰器

1.什么是装饰器

装饰器本质其实就是python函数或python类(因为可以通过函数和类实现装饰器)
装饰器本身是闭包函数的一种应用,因此也具备闭包函数的三个特性;

  • 必须是嵌套函数
  • 内层函数使用了外层函数中的非全局变量;可以是非上一层函数,对层级没要求
  • 外层函数返回下一层函数的引用;必须是下一层函数,对层级有要求


2. 装饰器的作用

1.在不改变源代码及调用方式的前提下,为其增加新的功能


3. 装饰器中的小知识点拆分


1.函数装饰器之变量的不释放

一般来说,函数执行完了,局部的整个名称空间都会被回收。但是如果名称空间中的某一个变量被其他地方引用到了,该变量所属的整个名称空间都不会被回收,而不是单指这个变量。另外外函数返回了内函数的地址引用给了一个全局变量,又因为全局变量在程序运行期间都不会被释放,所以全局变量(也就是内部函数引用)中所引用的外函数的临时变量不会被释放,相当于成了一个全局变量


2.装饰器嵌套嵌套三层的原因

image.png

一般来说,一个函数体中需要的传参是直接通过外部传递实参,函数类定义形参进行接收。但是会存在一种情况就是说,函数的参数是固定的,不允许为其添加新的参数,但是这个函数体又需要传参,我们就可以采用闭包的方式传参,闭包其实就是一种新的传参方式

遇到的问题:当函数体需要传参时,但是传参列表不能改动时(如下图)

下方报错原因:
如上图,首先内层的形参列表(*args,**kwargs)是要与被装饰函数test1的列表进行适应的,如果写在里面,那么将会报错

image.png

这种情况下,就采用传参的第二种方式,通过闭包进行传参(第一种是直接在当前函数传入形参)

那么我包第二层count_time(func),然后传参可不可以,结果如下,由于语法糖的特性,导致这里的参数也不能改

image.png

最终,只能选择包第三层,而第三层是能够接受所有参数的,之所以一二层不穿是因为固定用法,第三层就没这个限制了


3.@语法糖的作用


4. functools.wraps修复被装饰函数的结构问题

https://zhuanlan.zhihu.com/p/45535784


4. 类装饰器

类装饰器的应用:https://www.cnblogs.com/hiyang/p/12634734.html


5.多个装饰器装饰一个函数

加载顺序:从下到上

def deco1(func1):
    def wrapper1(*args, **kwargs):
        print('正在运行wrapper1第一行')
        res1 = func1(*args, **kwargs)
        print('正在运行wrapper1最后一行————————————')
        return res1

    return wrapper1


def deco2(func2):
    def wrapper2(*args, **kwargs):
        print('正在运行wrapper2第一行')
        res2 = func2(*args, **kwargs)
        print('正在运行wrapper2最后一行————————————')
        return res2

    return wrapper2


def deco3(func3):
    def wrapper3(*args, **kwargs):
        print('正在运行wrapper3第一行')
        res3 = func3(*args, **kwargs)
        print('正在运行wrapper3最后一行————————————')
        return res3

    return wrapper3


# 装饰器的加载顺序是从下往上,最近的先加载
@deco1  # cc=deco1(wrapper2的内存地址) ==>此时cc得到的是wrapper1的内存地址
@deco2  # cc=deco2(wrapper3的内存地址) ==>此时cc得到的是wrapper2的内存地址
@deco3  # cc=deco3(cc)  ==>此时cc得到的是wrapper3的内存地址
def cc():
    print('执行主代码')


# 可以直接输出cc的地址来确定
print(cc)  # <function deco1.<locals>.wrapper1 at 0x00000256F6B6A700>

# 执行顺序是从上往下,这个不用特别记住;由于加载顺序的原因,cc现在指向wrapper1,调用函数想当于调用wrapper1(),而wrapper1需要wrapper2的地址,所以会先往下执行,以此类推
cc()

运行结果

<function deco1.<locals>.wrapper1 at 0x0000024DFA37A700>
正在运行wrapper1第一行
正在运行wrapper2第一行
正在运行wrapper3第一行
执行主代码
正在运行wrapper3最后一行————————————
正在运行wrapper2最后一行————————————
正在运行wrapper1最后一行————————————

调用顺序图示例
image

posted @ 2021-07-03 22:47  中州韵  阅读(354)  评论(0编辑  收藏  举报