• 闭包与装饰器超详细


    54

    附加

    生命周期

    ​ 生命周期,每一个存储的数据都是有生命周期的

    def outer():
        a = 10
        print(f"a is {a}")
    #a的生命周期在outer中,出outer就到头了
    outer()
    print(a)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    变量解析原则 --LEGB

    • L – Local
    • E – Enclosing function locals
    • G – Global
    • B – Builtin

    闭包

    什么是闭包

    • 在函数中可以(嵌套)定义另一个函数时,如果内部的函数引用了外部的函数的变量,则可能产生 闭包。
    • 闭包可以用来在一个函数与一组“私有”变量之间创建关联关系。
    • 在给定函数被多次调用的过程中,这些私有变量能够保持其持久性。

    闭包条件

    1. 必须要有一个内嵌函数
    2. 内函数必须引用外函数的变量
    3. 外函数必须返回内函数
    #1.简单闭包
    def outer(x):
        a = 300
        def inner():
            print(f"两数之和为:{x+a}")
        return inner
    
    d = outer(10)    #d是inner
    d()
    #形成闭包dir(d)就会有__closure__属性
    #2。例子
    def outer():
        tmp_list = []
        def inner(name):
            tmp_list.append(1)
            print(f"{name} -- {tmp_list}")
        return inner
    
    d1 = outer()
    d2 = outer()
    d1("d1")
    d2("d2")
    d1("d1")
    print(id(d1),id(d2))
    print(id(outer()),id(outer()))
    输出:
    d1 -- [1]
    d2 -- [1]
    d1 -- [1, 1]
    1657755987984 1657758207280
    1924411675632 1924411675632 
    #虽然代码一样,但每次调用外函数,都会重新执行,创建一个新的tmp_list和inner
    #这里的outer()是一个匿名变量
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33

    闭包

    • 形成闭包之后,闭包函数会获得一个非空的__closure__属性(对比我们最后的函数test,test是一 个不具备闭包的函数,它的__closure__属性是None),这个属性是一个元组
    • 元组里面的对象为cell对象,而访问cell对象的cell_contents属性则可以得到闭包变量的当前值 (即上一次调用之后的值)
    • 而随着闭包的继续调用,变量会再次更新
    • 所以可见,一旦形成闭包之后,python确实会将__closure__和闭包函数绑定作为储存闭包变量的 场所

    装饰器

    什么是装饰器

    • 装饰器是这样一种设计模式:如果一个类(函数)希望添加其他类(函数)的一些功能,而不希望 通过继承或是直接修改源代码实现,那么可以使用装饰器模式

    • 简单来说Python中的装饰器就是指某些函数或其他可调用对象,以函数或类作为可选输入参数, 然后返回函数或类的形式。通过这个在Python2.6版本中被新加入的特性可以用来实现装饰器设计 模式

    • 装饰器就是一个可以接受调用也可以返回调用的函数,该函数接受被装饰的函数作为其位置参数。 装饰器通过使用该参数来执行某些操作,然后返回原始参数或一些其他的调用

    • 函数也是对象,也可以当做参数传递

      在这里插入代码片简而言之:装饰器就是在不改变函数或类的源代码的基础上,添加额外功能装饰器的本质就是闭包。它和闭包的不同是它需要传入一个callable对象

    #统计运行时间的装饰器
    import time
    def runtime(func):
        def inner():
            start = time.time()
            func()
            end = time.time()
            print(f"函数执行花了{end - start}s")
        return inner
    
    import time
    @runtime    #@修饰符去使用装饰器
    def func1():
        time.sleep(2)
        print("func1....")
    @runtime    #func2 = runtime(func2)
    def func2():
        time.sleep(2)
        print("func2....")
    func1()
    func2()
    输出:
    func1....
    函数执行花了2.000455856323242s
    func2....
    函数执行花了2.0076518058776855s
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26

    改良版(可以传递0-~参数,返回返回值)

    import time
    def runtime(func):
        print("this is runtime")
        def inner(*args,**kwargs):
            start = time.time()
            result = func(*args,**kwargs)
            end = time.time()
            print(f"函数执行花了{end - start}s")
            print(result)
            return result
        return inner
    
    import time
    @runtime    #@修饰符去使用装饰器
    def func1():
        time.sleep(2)
        print("func1....")
    @runtime    #func2 = runtime(func2)
    def func2(a,b):
        time.sleep(2)
        print(f"func2....{a},{b}")
        return a+b
    func1()
    print(func2(1,2))
    输出:
    this is runtime
    this is runtime
    func1....
    函数执行花了2.002455711364746s
    None
    func2....1,2
    函数执行花了2.009401559829712s
    3
    3
    
    username = ''
    def login_required(func):
        def inner(*args,**kwargs):
            if username == 'root':
                print("欢迎")
                result = func(*args,**kwargs)
                return result
            else:
                return '没有权限'
    
        return inner
    #可以应用多个装饰器,但是要注意装饰器的执行顺序
    #从最上面的装饰器的内函数开始执行
    @runtime  #add = runtime(login_required(add))
    @login_required
    def add(a,b):
            return a+b
    
    username = input("请输入你的username:")
    print(add(1,2))
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55

    编写和使用装饰器

    什么是元数据

    ​ 元数据(Metadata),又称中介数据、中继数据,为描述数据的数据(data about data),主要是 描述数据属性(property)的信息(函数的重要的元信息比如名字、文档字符串、注解和参数签名等)

    保留元数据

    ​ 利用@functools.wraps(fun),将一个函数的重要内容复制到另一个函数

    ​ 温馨提醒:任何时候你定义装饰器的时候,都应该使用 functools 库中的 @wraps 装饰器来注解底层 包装函数

    内置的装饰器

    Python还提供了类装饰器与@staticmethod,@classmethod,@property和这三个在面向对象编 程中常用的装饰器

    • staticmethod:把类中定义的实例方法变成静态方法-
    • classmethod:把类中定义的实例方法变成类方法
    • property:把类中定义的实例方法变成类属性
    import functools
    import time
    def runtime(func):
        print("this is runtime")
        #保留传递进来的函数的元数据,将他的元数据赋值给Inner
        @functools.wraps(func)
        def inner(*args,**kwargs):
            start = time.time()
            result = func(*args,**kwargs)
            end = time.time()
            print(f"函数执行花了{end - start}s")
            print(result)
            return result
        return inner
    username = ''
    def login_required(func):
        def inner(*args,**kwargs):
            if username == 'root':
                print(f"欢迎执行{func.__name__}函数")
                result = func(*args,**kwargs)
                return result
            else:
                return f'执行{func.__name__}权限不够'
    
        return inner
    #可以应用多个装饰器,但是要注意装饰器的执行顺序
    #从最上面的装饰器的内函数开始执行
    @login_required
    @runtime  #add = runtime(login_required(add))
    def add(a,b):
            return a+b
    
    username = input("请输入你的username:")
    print(add(1,2))
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34

    装饰器带参数

    • 自身不传入参数的装饰器,使用两层函数定义
    • 自身传入参数的装饰器,使用三层函数定义
    def deco1(name):
        def login_required(func):
            def inner(*args,**kwargs):
                if name == 'root':
                    print(f"欢迎执行{func.__name__}函数")
                    result = func(*args,**kwargs)
                    return result
                else:
                    return f'执行{func.__name__}权限不够'
            return inner
        return login_required
    #login_required = deco1(name = "root")
    # add = login_required(add)
    name = input("请输入你的name:")
    @deco1(name)
    def add(a,b):
            return a+b
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    用类实现装饰器

    #基于类实现装饰器
    import time
    class A:
        def __init__(self,func):
            self.func = func
        def __call__(self, *args, **kwargs):    #-->inner
            start = time.time()
            print("this is call")
            result = self.func(*args, **kwargs)
            time.sleep(1)
            end = time.time()
            print(end - start)
            return result
    
    @A
    def func1(a,b):
        print("i am func1...")
        return a+b
    
    print(func1(1,2))
    
    #带参数的
    import time
    class A:
        def __init__(self,name):
            self.name = name
        def __call__(self,func):    #-->inner
            def deco(*args,**kwargs):
                start = time.time()
                result = func(*args, **kwargs)
                end = time.time()
                print(f"函数执行花了{end - start}")
                return result
            return deco
    
    @A(name = input("请输入name:"))
    #a1 = A("name")
    #func1 = a1(func1)
    def func1(a,b):
        print("i am func1...")
        return a+b
    
    print(func1(1,2))
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43

    装饰类

    def outer(cls):
        def inner(*args,**kwargs):
            print(f"class name is:{cls.__name__}")
            return cls(*args,**kwargs)
        return inner
    
    @outer    #A = outer(A)
    class A:
        def __init__(self,name):
            self.name = name
    
    print(type(A))
    m = A("sc")
    print(m.name)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
  • 相关阅读:
    批处理编写
    CleanMyMac X4.11.1中文正式版 系统优化 垃圾清理 程序卸载工具
    一套即学即用,受益终身的效率思维
    mysql整库备份表结构和数据
    WEB前端网页设计 HTML CSS 网页设计参数 - 【边框圆角】
    Ant vue中表单验证(动态校验、部分校验)
    编辑器库QsciScintilla的indicator点击没有按键信息的bug解决
    P02014186陈镐镐
    稳定性总结
    LeetCode 面试题 16.25. LRU 缓存
  • 原文地址:https://blog.csdn.net/a1991376352/article/details/126062470