• 【Python】高级语法——闭包和装饰器


    来源:
    【参考:Python必知必会 - 闭包和装饰器
    【参考:Python的闭包和装饰器,我是这样理解的_哔哩哔哩_bilibili

    在原有基础上添加自己的思考过程

    代码可视化网站:【参考:Python Tutor: Learn Python, JavaScript, C, C++, and Java by visualizing code

    总结

    1. LEGB查找原则(局部,嵌套,全局,内置作用域)
    2. Python闭包(def 嵌套一个 def,特点是能记住当时的嵌套作用域
    3. Python装饰器(一种特殊的闭包,给多个函数添加相同的功能,如计时功能)
    • def语句在这个模块文件顶层将一个函数对象赋值给变量名func
    • def是一个简单的可执行语句,可以出现在任意其他语句能够出现的地方,包括嵌套在另一个def之中。
    • 变量的作用域只与定义的位置有关,与函数调用位置无关

    LEGB

    Local、Enclosing function locals、Global、Built-in

    当引用一个变量时,Python按以下顺序依次进行查找:

    1. 本地作用域 L
    2. 上层嵌套函数的作用域 E
    3. 全局作用域 G
    4. 内置作用域 B

    第一个能够完成查找的就算成功,就会停止往下查找

    s = "G"  # 3. (G) 全局作用域
    
    def computer(value=20):
        # 嵌套作用域内的变量:value, prize函数, s
        def prize():
            print("s:", s)  # 1.(L) 本地变量
            print("inner:", value)
    
        s = "E"  # 2.(E) 上一层结构中的def或lambda的本地作用域
        prize()  # 变量的作用域只与定义的位置有关,与函数调用位置无关
    
    computer(88)
    
    """
    s: E
    inner: 88
    """
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    word = "global"
    
    def outer(word="TAT"):
        # 作用域:word(参数), inner函数
        print(word) # L
        def inner():
            print(word) # E
        word += "enclose"
    
        inner()
    
    outer()
    print(word) # G
    
    """
    TAT
    TATenclose
    global
    
    """
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    闭包

    【参考:python闭包的作用_whgiser的博客-CSDN博客

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

    未使用闭包

    # 为什么我们要引入闭包?
    func_list = []
    for k in range(1, 5):
        def func(x):
            return x * k
        func_list.append(func) # 只存入函数名,参数的值没有存入
    
    # 上面运行完后 k=4
    
    for func in func_list:  # 希望是 1x,2x,3x,4x
        print(func(3))      # 希望是 3, 6, 9, 12
        # 结果为 3*4=12
    
    """
    12
    12
    12
    12
    """
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    引入闭包

    # 使用闭包解决上述问题
    def enclose(k):
    	# 嵌套作用域:参数k,函数func
        def func(x):
            return x * k
        return func
    
    func_list = []
    for k in range(1, 5):
        func_list.append(enclose(k))  # 闭包能记住当时的作用域 这里存入了函数和参数值
    
    for func in func_list:  # 1x,2x,3x,4x
        print(func(3))      # 3, 6, 9, 12
    
    """
    3
    6
    9
    12
    """
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    备注:闭包还是记不住全局作用域内的变量的

    # ============================================
    # == 闭包可以记住嵌套作用域(即上一层作用域)
    # ============================================
    T = 1
    
    def enclose(day):
        # 嵌套作用域 变量day,函数func
        day = day + T
        def func(x):
            print("day: ", day) # 1. 嵌套作用域内的变量day可以记住
            print(x + T) # 2. 全局变量T仍然无法记住,只有在调用func函数时才能使用T的值
        return func
    
    f = enclose(day=7) 
    # 这个函数返回一个变量名func并赋值给f,并且func记住了当时enclose嵌套作用域内的变量day
    
    """
    全局变量T=1
    day = 7+1 =8
    """
    
    T += 1
    
    g = enclose(day=7)
    """
    全局变量T=2
    day= 7+2 =9
    """
    
    
    f(x=1)
    """
    全局变量T=2
    func():
        
        x+T = 1+2
    """
    g(x=1)
    """
    全局变量T=2
    func():
    
        x+T = 1+2
    """
    
    输出:
    """
    day:  8
    3
    day:  9
    3
    """
    
    • 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

    装饰器

    装饰器就是个闭包(只不过外层def传入的参数是个函数变量),只不过多了一个语法糖

    装饰器让你在一个函数的前后去执行代码。

    import time
    
    # 装饰器函数
    def outer(origin): 
        # origin是一个函数名
        def inner():  
            t1 = time.time()
            origin()
            t2 = time.time()
            print(f"finish func: {origin.__name__}, time cost: {t2 - t1:.6f}(sec)")
        return inner
    
    # --------1----------
    def myfunc1():
        for i in range(100000):
            pass
        print("我是myfunc1函数")
    
    func = outer(myfunc1) 
    """
    相当于
    origin = myfunc1
    func = inner
    """
    
    func()
    """
    相当于调用inner()
    然后会执行origin()即myfunc1()
    
    """
    
    # --------2---------- 装饰器
    # 语法糖 使用装饰器
    @outer # myfunc2= outer(myfunc2) 在def之后执行
    def myfunc2(): 
        for i in range(100000):
            pass
        print("我是myfunc2函数")
        
    myfunc2()
    
    """
    我是myfunc1函数
    finish func: myfunc1, time cost: 0.001995(sec)
    我是myfunc2函数
    finish func: myfunc2, time cost: 0.001994(sec)
    """
    
    • 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

    典型的装饰器写法

    import time
    
    
    def outer(origin):
        def inner(*args, **kwargs):
            t1 = time.time()
            origin(*args, **kwargs) # 需要接收参数
            t2 = time.time()
            print(f"finish func: {origin.__name__}, time cost: {t2 - t1:.6f}(sec)")
        return inner
    
    
    @outer # func1_new = outer(func1) 在def之后执行
    def func1(a1):
        print("我是func1函数")
        print(a1)
        value = 11
        return value
    """
    相当于
    origin=func1
    func1_new =inner
    """
    func1(-1) # func1_new 
    """
    相当于调用inner(-1) 
    然后会执行origin(-1)即func1(-1)
    """
    
    @outer
    def func2(b1,b2):
        print("我是func2函数")
        print(b1,b2)
        value = 22
        return value
    
    
    @outer
    def func3(s="special"):
        print("我是func3函数")
        value = 33
        return value
    
    
    
    func2(8, 9)
    func3(s="time")
    
    • 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
    我是func1函数
    -1
    finish func: func1, time cost: 0.000000(sec)
    我是func2函数
    8 9
    finish func: func2, time cost: 0.000000(sec)
    我是func3函数
    finish func: func3, time cost: 0.000000(sec)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    多层装饰器

    import logging
    
    logging.basicConfig()
    
    # 声明一个 Logger 对象
    logger = logging.getLogger(__name__)
    
    # 设置忽略级别
    logger.setLevel(level=logging.INFO)
    
    # 嵌套的装饰器
    def use_logging(level):
        def decorator(func):
            def wrapper(*args, **kwargs):
                if level == "info":
                    logger.info("%s is running" % func.__name__)
                func(*args)
            return wrapper
    
        return decorator
    
    def foo(name='foo'):
        print("i am %s" % name)
    
    foo = use_logging(level='warn')(foo)
    foo()
    
    # ============= 等价 ==============
    
    # python语法糖
    # foo = use_logging(level='warn')(foo)
    # 可以看出@后面的内容就是上行代码去掉左边的foo和右边的(foo)剩下的内容
    @use_logging(level='warn')
    def foo(name='foo'):
        print("i am %s" % name)
    
    """
    level='warn'
    func=foo
    foo_new = decorator
    """
    foo() # foo_new
    """
    相当于调用decorator()
    然后会执行wrapper(),wrapper里面会执行func()即foo()
    """
    
    • 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
    i am foo
    i am foo
    
    • 1
    • 2

    类装饰器

    class myDecorator(object):
        def __init__(self, f):
            print("inside myDecorator.__init__()")
            f()  # Prove that function definition has completed
            self.g = f  # 将函数变量f设置为类属性g
    
        def __call__(self):
            print("inside myDecorator.__call__()")
            self.g()
    
    
    def aFunction():
        print("inside aFunction()")
    
    
    aFunction = myDecorator(aFunction)  # aFunction是一个类实例化后的对象
    
    print("=== Finished decorating aFunction() ===")
    aFunction() # 会调用call
    
    print('------------------------------')
    @myDecorator
    def bFunction():
        print("inside bFunction()")
    bFunction()
    
    • 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
    inside myDecorator.__init__()
    inside aFunction()
    === Finished decorating aFunction() ===
    inside myDecorator.__call__()
    inside aFunction()
    ------------------------------
    inside myDecorator.__init__()
    inside bFunction()
    inside myDecorator.__call__()
    inside bFunction()
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    对象装饰器

    class Decorator:
        def __init__(self, arg1, arg2):
            print('执行类Decorator的__init__()方法')
            self.arg1 = arg1
            self.arg2 = arg2
    
        def __call__(self, f):
            print('执行类Decorator的__call__()方法')
    
            # wrap = xxx
            def wrap(*args):
                print('执行wrap()')
                print('装饰器参数:', self.arg1, self.arg2)
                print('执行' + f.__name__ + '()')
                f(*args)  # 执行顾客的功能
                print(f.__name__ + '()执行完毕')
    
            return wrap
    
    
    @Decorator('Hello', 'World')  # 2. example = Decorator('Hello', 'World')(example)
    def example(a1, a2, a3):  # 1. example = xxx
        print('传入example()的参数:', a1, a2, a3)
    
    
    print('装饰完毕')
    
    print('准备调用example()')
    example('Wish', 'Happy', 'EveryDay')  # 3.
    print('测试代码执行完毕')
    
    • 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
    执行类Decorator的__init__()方法
    执行类Decorator的__call__()方法
    装饰完毕
    准备调用example()
    执行wrap()
    装饰器参数: Hello World
    执行example()
    传入example()的参数: Wish Happy EveryDay
    example()执行完毕
    测试代码执行完毕
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    wraps

    未使用wraps

    from functools import wraps
    
    
    def my_decorator(func):
        # @wraps(func)  # 防止被装饰函数example的函数名称和docstring被修改
        def wrapper(*args, **kwargs):
            '''decorator'''
            print('Calling decorated function...')
            func(*args, **kwargs)
    
        return wrapper
    
    
    @my_decorator
    def example(): # 被装饰函数
        """Docstring"""
        print('Called example function')
    
    
    example()
    print(example.__name__, example.__doc__)
    
    """
    Calling decorated function...
    Called example function
    wrapper decorator
    """
    
    
    • 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

    使用wraps

    from functools import wraps
    
    
    def my_decorator(func):
        @wraps(func)  # 防止被装饰函数example的函数名称和docstring被修改
        def wrapper(*args, **kwargs):
            '''decorator'''
            print('Calling decorated function...')
            func(*args, **kwargs)
    
        return wrapper
    
    
    @my_decorator
    def example(): # 被装饰函数
        """Docstring"""
        print('Called example function')
    
    
    example() 
    print(example.__name__, example.__doc__)
    
    """
    Calling decorated function...
    Called example function
    example Docstring
    """
    
    
    • 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

    装饰器模板

    from functools import wraps
    
    
    def outer(origin):
        @wraps(origin)
        def inner(*args, **kwargs):
            print("before ...") # 执行前
            sum = origin(*args, **kwargs)  # 调用原来的func函数
            print("after ...") # 执行后
            return sum
        return inner
    
    
    @outer  # func = outer(func)
    def func(x, y):
        print(f"x: {x}, y: {y}")
        return x + y
    
    
    res = func(3,7)
    print(res)
    """
    before ...
    x: 3, y: 7
    after ...
    10
    """
    
    • 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
  • 相关阅读:
    Java反射机制
    浅谈云上攻防系列——云IAM原理&风险以及最佳实践
    Rowset Class
    云原生之深入解析Jenkins多分支管道
    压线生填报志愿不用愁,看这一篇就够了
    什么是 WebXR Device API?
    c 实用化的摄像头生成avi视频程序(加入精确的时间控制和随时停止录像功能)
    DCA培训心得笔记(二)
    【Java】绘图入门和机制,绘图方法演示(绘制坦克)
    theos tweak导入自定义类
  • 原文地址:https://blog.csdn.net/Jruo911/article/details/125449768