美文网首页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