• 深入理解闭包:原理、应用与最佳实践


    1、 什么是闭包?

    如果一个函数内部定义了另一个函数,并且内部函数引用了外部函数的变量,那么内部函数就形成了一个闭包。

    def outer_function(x):
        # 外部函数接受一个参数 x 是自由变量
        # seed 也是一个自由变量
        seed = 10
    
        def inner_function(y):
            # 内部函数接受另一个参数 y
            return x + y + seed
    
        return inner_function
    
    
    # 创建闭包函数,传入参数 10,closure就是一个闭包
    closure = outer_function(10)
    
    # 使用闭包函数计算 5 + 10 + 10
    result = closure(5)
    print(result)  # 输出 25
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    2、自由变量

    https://docs.python.org/zh-cn/3.7/reference/executionmodel.html#index-6
    局部变量,如果名称绑定在一个代码块中,则为该代码块的局部变量。
    全局变量,如果名称绑定在模块层级,则为全局变量。
    自由变量,如果变量在一个代码块中被使用但不是在其中定义,则为 自由变量。

    3、闭包的特点

    1、 闭包可以**捕获(即使外部函数已经执行完毕,这些变量依然可以被内部函数访问和操作)外部变量,**并且保持外部变量的状态,使其在多次调用中保持不变。
    2、闭包允许函数返回一个函数,而不仅仅是一个值。
    3、闭包与闭包之间的状态是隔离的

    def average():
        data = []  # 使用列表来存储内部状态
    
        def add_number(number):
            data.append(number)  # 将新数字添加到列表中
            total = sum(data)  # 计算列表中所有数字的总和
            count = len(data)  # 获取列表中数字的数量
            return total / count if count > 0 else 0  # 计算平均数
    
        return add_number
    
    
    # 创建累计平均数的闭包
    avg = average()
    
    # 不断添加新的数字并计算平均数
    # data变量是average函数的局部变量
    # 但是 当调用avg(10)时,average函数已经执行完了,所以它的作用域已经不存在了
    print(avg(10))  # 平均数: 10.0
    print(avg(20))  # 平均数: 15.0
    print(avg(30))  # 平均数: 20.0
    print("avg --> ", avg(40))  # 平均数: 25.0
    
    # 闭包和闭包之间的状态是隔离的
    avg1 = average()
    print("avg1 --> ", avg1(11))
    print("avg1 --> ", avg1(15))
    
    
    • 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

    4、闭包的应用

    本地作用域在函数结束后就立即失效,而嵌套作用域在嵌套的函数返回后却仍然有效,类似可以把这些变量类比为 C++中局部静态变量。想要给函数增加或者保持状态、实现装饰器、构建工厂函数、创建函数组合就可以使用闭包来实现。

    ##################### 函数组合
    def add(x):
        return x + 2
    
    def multiply(x):
        return x * 3
    
    def compose(f, g):
        # 返回一个闭包,将 f(g(x)) 的结果
        def inner(x):
            return f(g(x))
        return inner
    
    # 创建函数组合
    combined_function = compose(add, multiply)
    
    # 使用组合函数
    result = combined_function(4)  # 先执行 multiply(4),然后执行 add(12)
    print(result)  # 输出 14
    
    
    ################### 创建工厂函数
    def create_multiplier(factor):
        # 工厂函数返回一个闭包
        def multiplier(x):
            return x * factor
        return multiplier
    
    # 创建两个不同的乘法函数工厂
    double = create_multiplier(2)
    triple = create_multiplier(3)
    
    # 使用工厂函数生成乘法函数
    double_result = double(5)  # 返回 5 * 2 = 10
    triple_result = triple(5)  # 返回 5 * 3 = 15
    
    print(double_result)
    print(triple_result)
    
    ################# 装饰器
    import time
    
    def timing_decorator(func):
        def wrapper(*args, **kwargs):
            start_time = time.time()
            result = func(*args, **kwargs)
            end_time = time.time()
            execution_time = end_time - start_time
            print(f"{func.__name__} executed in {execution_time:.4f} seconds")
            return result
        return wrapper
    
    # 使用装饰器
    @timing_decorator
    def requests_http_data():
        # 模拟一些耗时操作
        time.sleep(2)
    
    requests_http_data()
    
    
    • 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
    • 56
    • 57
    • 58
    • 59
    • 60

    5、global和nonlocal

    global 声明对全局变量进行引用修改
    nonlocal 内嵌函数内部想对嵌套作用域中的值是不可变类型的变量(值为 int、float、str)进行修改

    n = 100
    
    def add():
        global n # 函数内部要对全局变量进行修改,必须使用global声明
        n = n +100
        print(n)
    
    add()
    print(n)
    
    
    def sub():
        a = 100
    
        def execs():
            nonlocal a # 内嵌的函数想修改外部函数的变量,必须使用nonlocal进行声明
            a = a - 1
            return a
        return execs
    
    s = sub()
    print(s())
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    6、闭包和类

    闭包比较像只有一个方法的类,可以保持状态和数据隐藏,为什么不写成类:
    1、闭包的功能一般很小很简单
    2、闭包执行速度较快,不需要多余的self参数等

    # 闭包
    def counter():
        count = 0
    
        def increment():
            nonlocal count
            count += 1
            return count
    
        return increment
    
    # 创建闭包对象
    counter1 = counter()
    counter2 = counter()
    
    print(counter1())  # 输出 1
    print(counter1())  # 输出 2
    print(counter2())  # 输出 1
    print(counter1())  # 输出 3
    
    # 类
    class Counter:
        def __init__(self):
            self.count = 0
    
        def increment(self):
            self.count += 1
            return self.count
    
    # 创建类对象
    counter1 = Counter()
    counter2 = Counter()
    
    print(counter1.increment())  # 输出 1
    print(counter1.increment())  # 输出 2
    print(counter2.increment())  # 输出 1
    print(counter1.increment())  # 输出 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

    7、扩展-偏函数

    # 偏函数,也可以保持函数内部的变量状态
    # 我们可以使用内置的 functools 模块的 partial 函数来创建偏函数。
    # 偏函数指通过固定函数的一部分参数后,返回一个新的函数,
    # 这个新函数可以接受剩余的参数进行调用
    from functools import partial
    
    
    def add(a, b):
        return a + b
    
    
    x = partial(add, 1) # 1赋给参数a 并暂停函数
    print(x)
    res1 = x(2)	# 将2赋给b后进行计算
    print(res1) # 3
    res2 = x(3)
    print(res2) # 4
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
  • 相关阅读:
    Spring MVC 框架学习(八)---- SSM 框架整合
    【Android】SDK安装及配置
    Redis 命令—— 超详细操作演示!!!
    [cocos creator] Label设置为RESIZE_HEIGHT,获取height
    实操自动生成接口自动化测试用例
    Python3,这个方法,应该是读取文件天花板了。
    Java 的下载安装教程
    Python实现简单的爬虫功能
    【STM32学习】工程模板&跑马灯
    【软考】-- 操作系统(上)
  • 原文地址:https://blog.csdn.net/u010442378/article/details/133916901