zoukankan      html  css  js  c++  java
  • 装饰器

    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

    本文来自博客园,作者:中州韵,转载请注明原文链接:https://www.cnblogs.com/zhongzhouyun/p/14967840.html

  • 相关阅读:
    vue——图片懒加载v-lazy
    vue——利用intersectionOberver实现全局appear/disappear事件
    WXS-----学会使用WXS
    使用内联样式
    样式引入
    小程序开发框架----WXSS
    引入内部外部模板
    Selenium元素定位的几种方式
    Response Assertion(响应断言)
    参数化CSV Data Set config元件
  • 原文地址:https://www.cnblogs.com/zhongzhouyun/p/14967840.html
Copyright © 2011-2022 走看看