美文网首页python学习交流
Python 关键知识4:闭包

Python 关键知识4:闭包

作者: 水之心 | 来源:发表于2018-12-06 14:42 被阅读11次

    变量作用域

    从内层函数的角度看,变量使用的两个维度

    • 是否能访问:LEGB 规则
    • 是否能修改:需要声明才能修改

    变量作用域识别三要素

    • 出现位置:在哪里访问了
    • 赋值位置:在哪里赋值了
    • 声明类型:在哪里声明了

    三种变量作用域(声明)

    • 局部: local (函数调用时才创建)
    • 全局: global (导入模块时创建,直至解释器退出)
    • 非全局: nonlocal

    划重点:

    • 无声明的情况下,赋值即私有,若外部有相同变量名则将其遮挡
    • 无赋值情况下,变量访问依据 LEGB 法则
    • 内层函数可以通过声明的方式直接修改外部变量
    • 位于最内层的函数,通过 global 声明,会越过中间层,直接修改全局变量
    • global 声明其实是一种绑定关系,意思是告诉解释器,不用新创建变量了,我用的是最外面那个
    • 位于最内层的函数,如果仅想修改中间层变量,而不是全局变量,可使用 nonlocal 关键字
    • nonlocal 只能绑定在中间层定义的变量,如果中间层变量被声明外全局变量,则会报错

    金句:

    • 无声明的情况下,赋值即私有,若外部有相同变量名则将其遮挡
    • 想修改外部相同变量名,需要将外部变量声明
    • 根据外部变量的作用域级别不同,使用 global 或者 nonlocal

    为什么会有 nonlocal 关键字?

    • nonlocal 填补了 globallocal 之间的空白
    • nonlocal 的出现其实是一种权衡利弊的结果:私有之安全封装,全局之灵活共享
    • nonlocal 是 Python 3 中引入的一个官方的解决方案,以弥补内层函数无法修改中间层不可变对象

    什么是闭包?

    • 定义:延伸了作用域的函数(能访问定义体之外定义的非全局变量
    • 闭包是一种函数,它会保留定义函数时存在的外层非全局变量的绑定

    闭包有什么用呢?

    1. 共享变量的时候避免使用了不安全的全局变量
    2. 允许将函数与某些数据关联起来,类似于简化版面向对象编程
    3. 相同代码每次生成的闭包,其延伸的作用域都彼此独立(计数器,注册表)
    4. 函数的一部分行为在编写时无法预知,需要动态实现,同时又想保持接口一致性
    5. 较低的内存开销:类的生命周期远大于闭包
    6. 实现装饰器(装饰器的本质是一个闭包,而 @ 仅仅是一个语法糖)

    下面我们要实现一个任务:每次调用便计算之前所有的数的平均值。

    实现版本1:类实现

    class Averager:
        def __init__(self):
            self.series = []
    
        def __call__(self, new_value):
            '''
            令类表现的像函数一样,可调用
            '''
            self.series.append(new_value)
            total = sum(self.series)
            return total / len(self.series)
    
    
    avg = Averager()
    avg(10), avg(11), avg(21)
    
    (10.0, 10.5, 14.0)
    

    版本2:闭包实现

    def make_averager():
        series = []
    
        def averager(new_value):
            series.append(new_value)
            total = sum(series)
            return total / len(series)
        return averager
    
    
    avg = make_averager()
    avg(10), avg(11), avg(21)
    
    (10.0, 10.5, 14.0)
    

    由于每次都需重新计算总数,做了许多不必要的重复,为了简化版本2,我们可以这样:

    版本3:优化的闭包实现

    def make_averager():
        count = 0
        total = 0
    
        def averager(new_value):
            nonlocal count, total
            count += 1
            total += new_value
            return total / count
    
        return averager
    
    avg = make_averager()
    avg(10), avg(11), avg(21)
    
    (10.0, 10.5, 14.0)
    

    一步一步教你认识Python闭包

    相关文章

      网友评论

        本文标题:Python 关键知识4:闭包

        本文链接:https://www.haomeiwen.com/subject/ntlocqtx.html