• Python中如何理解闭包


    闭包,是函数式编程中的一个较难理解的概念,JavaScript、Python、Perl、Object-C等很多语言都支持闭包。

    说白了, 闭包就是函数嵌套函数,在函数中定义并返回另一个函数
    例如:

    def func1():
        def func2():
            pass
        return func2
    

    通常情况下,我们的函数直接定义在模块中,模块中的变量称为全局变量,用户及任何函数都可以直接使用并修改。
    如果把模块比作大厅的话,那么闭包就是包间。闭包中的变量,则只有内部函数能直接使用。
    例如:

    printer1 = '打印机1'  # 全局变量,用户及所有函数可以使用和修改
    
    def room1():   # 闭包函数
         printer2 = '打印机2'  # 闭包函数中的变量称为 自由变量
         
        def a():
            pc1 = '电脑1'   # 局部变量,只有当前函数使用
            print('我可以使用全局变量', printer1)
            print('我可以使用自由变量', printer2)
            print('我可以使用自己的局部变量', pc1)
    
        return a  # 不return 函数 a,则用户没办法使用这个函数
            
    def b():
        pc1 = '电脑2'   # 局部变量,只有当前函数使用
        print('我可以使用全局变量', printer1)
        print('我可以使用自己的局部变量', pc2)
    
    a = room1()  # 调用闭包函数返回内部函数
    a()  # 内部函数可以使用room1中的变量printer2
    b()  # 调用普通函数b,b没办法使用room1中的变量
    

    作用域链的原则是内部可以使用外部变量,外部不能使用内部变量
    使用闭包函数可以为函数开辟一个独立空间,其中的变量外部无法直接使用,只能通过函数a间接使用。因此闭包可以起到隐藏变量的作用,可以防止变量被修改和破坏。

    为什么闭包函数room1中的变量printer2不是局部变量而称为“自由变量”呢?
    我们知道,函数调用结束后即终止,其中的局部变量会被清理。如调用b函数结束后,其中的局部变量pc1就会清理回收(下次调用重新创建)。
    而闭包函数room1调用后得到a函数,此时room1函数已经调用结束,但由于a函数还要使用变量printer2,因此printer2不会被清理,而是一直为a函数保留着。
    因此,闭包会加大内存开销,甚至造成内存泄露
    然而隐藏和保护变量是很重要的,比如一个存钱和取钱的功能,两个都使用需要一个名为balance的余额变量,如果使用全局变量,示例如下:

    balance = 0  # 初始余额
    
    def save(amount):   # 存钱
        global balance    # 非容器全局变量,需要显示声明其为全局变量才能修改
        if amount > 0:
            balance += amount  # 余额增加
            print('存入 %d 当前余额 %d' %(amount, balance))
        else:
            print('amount必须大于0')
    
    def draw(amount):  # 取钱
        global balance
        if amount > 0:
            if balance > amount:
                balance -= amount  # 余额减少
                print('取出 %d 当前余额 %d' % (amount, balance))
            else:
                print('余额不足')
        else:
            print('amount必须大于0')
    
    amount = 1000000  # 直接修改余额
    draw(200)
    

    对于非容器(如列表、字典)类型的变量balance,不能直接进行修改。
    在函数中如果直接使用如balance += amount则会视为新建了一个同名的局部变量。
    因此需要使用global balance进行声明其为全局变量。

    使用全局变量的危险是,用户可以直接修改amount的值,不经过save存钱,便可任意draw取钱。
    我们可以使用闭包来隐藏balance变量,例如:

    def bank():
        balance = 0  # 初始余额
    
        def save(amount):   # 存钱
            nonlocal balance  # 声明不是save中的局部变量,即bank中的自由变量balance
            if amount > 0:
                balance += amount  # 余额增加
                print('存入 %d 当前余额 %d' %(amount, balance))
            else:
                print('amount必须大于0')
    
        def draw(amount):  # 取钱
            nonlocal balance  # 声明是bank中的自由变量
            if amount > 0:
                if balance > amount:
                    balance -= amount  # 余额减少
                    print('取出 %d 当前余额 %d' % (amount, balance))
                else:
                    print('余额不足')
            else:
                print('amount必须大于0')
        return save, draw  # 返回两个函数,以使得用户可以使用这两个函数
    
    save, draw = bank()  # 使用闭包得到两个功能函数
    save(100)  # 存钱
    draw(200)  # 取钱 (显示余额不足)
    

    同全局变量一样,如果save、draw中直接修改balance,同样会视为新建了一个同名的局部变量,此时需要使用nonlocal来声明其不是局部变量。

    为什么我们在Python中很少用到闭包?因为Python还支持类和对象呀,使用类可以轻松实现独立空间和变量私有问题。

    在Python中,闭包常用作装饰器,在一个装饰器函数中定义一个新的函数来替代原函数。

  • 相关阅读:
    Python 华为云OSS建桶与文件上传下载删除及检索示例
    Sonar 扫描之分析参数介绍
    浅谈探寻企业数字化
    关于碳中和的一点浅谈
    ElementUI 多选+远程搜索
    vue组件之间共享数据
    vue 音频组件
    vue 视频播放
    在 windows环境下 python 调试信息 输出彩色的字
    MySQL CPU使用彪高,如何快速找到源头
  • 原文地址:https://www.cnblogs.com/superhin/p/16104662.html
Copyright © 2020-2023  润新知