闭包

闭包(Closure)是Python中的高级概念,它只出现在嵌套函数中,但和嵌套函数有很大的不同,那就是闭包使用了一个叫自由变量的东西。

实际中,闭包就是一个函数,只不过这个函数可以引用没有在该函数内部定义的变量,这个函数本身可以是匿名函数也可以是非匿名函数。

光说不练假把式,下面通过一个例子理解一下。

def make_averager():series = []def averager(new_value):series.append(new_value)print("series in averager: ", series)total = sum(series)return total / len(series)return averager

上面定义了一个函数 make_averager,这个函数的返回值是一个函数对象 averager,谨记“Python中一切皆可对象”的真理。当每次 averager 被调用的时候,它就会接收一个新的值,并把这个值添加到 series 数组中,并计算此时数组的平均值。这个函数到底有什么威力呢?再来看下面的使用例子。

avg = make_averager()
print(avg(10))
print(avg(11))

输出结果如下:

series in averager:  [10]
10.0
series in averager:  [10, 11]
10.5

看出端倪了吗? 通过调用 avg(n) 就可以实现更新历史 series 并且计算当前 series 的平均值。那么到底是如何实现更新 series 呢?讲道理,series 是 make_averager 函数中定义的一个局部变量,并且当通过 avg(n)调用时,明显 make_averager已经被执行过了,应该就访问不到 series 变量了。

实际上在嵌套函数 averager 中,series 是一个自由变量(free variable),表明这个变量没有被绑定在局部作用域中,同时 averager函数的闭包行为使得它可以访问到这个自由变量。

这里的自由变量 series 保存在函数的只读属性 __closure__ 中,我们可以打印看一下:

print(avg.__closure__)
(<cell at 0x11822a8e8: list object at 0x1166541c8>,)print("####:",avg.__closure__[0].cell_contents)
####: [10, 11]

可以看到 __closure__存储的是一个 list 对象,通过第二个 print 也可以看到当前 series 中的两个元素。

如果这个函数仅仅是嵌套函数,那么它的 __closure__ 应该是 None。

nonlocal

上面代码中的 series 是可变变量,在内部函数中使用时没有问题,但如果是不可变变量,如 string、number、tuple 等,在内部函数引用则会报错。还是上面的例子,将它稍微改写一下,变成下面的代码,好处是不用每次都重新计算 series 的 sum。

def make_averager():total = 0count = 0def averager(new_value):count += 1total += new_valuereturn total / countreturn averageravg = make_averager()
print(avg(10))

这时运行代码则会报错,报错信息如下:

UnboundLocalError                         Traceback (most recent call last)
<ipython-input-323-5c1ff34d8622> in <module>9 10 avg = make_averager()
---> 11 print(avg(10))12 13 print(avg.__closure__)<ipython-input-323-5c1ff34d8622> in averager(new_value)3     count = 04     def averager(new_value):
----> 5         count += 16         total += new_value7         return total / countUnboundLocalError: local variable 'count' referenced before assignment

问题出在 count+=1 这行代码,为什么呢?因为在Python中并没有要求先声明一个变量,所以Python解释器任务在函数体内,只要对一个变量进行赋值操作,那么这个变量就是局部变量。而 count+=1相当于 count=count+1,对 count 进行了赋值操作,所以Python解释器认为 count 是函数内的局部变量,但是从执行顺序上来说,count+1则是先执行的,先引用了局部变量 count,即使外面有一个同名的 count 变量。并且此时 count 就不再是自由变量了,也不会存储在 __closure__中。

那么如何解决这个问题呢?Python3中引入了 nonlocal关键字,它可以使得一个被赋值的局部变量变为自由变量,并且 nonlocal声明的变量发生变化时,__closure__中存储的值也会发生变化。所以只要对 count 及 total 变量加上 nonlocal的声明就可以了,即:

def make_averager():total = 0count = 0def averager(new_value):nonlocal count, totalcount += 1total += new_valuereturn total / countreturn averageravg = make_averager()
print(avg(10))
print("####:",avg.__closure__[0].cell_contents, avg.__closure__[1].cell_contents)print(avg(11))
print("####:",avg.__closure__[0].cell_contents, avg.__closure__[1].cell_contents)

运行结果为:

10.0
####: 1 10
10.5
####: 2 21

打印 __closure__ 的值,也可以发现确实发生了变化。

Python-闭包与自由变量相关推荐

  1. python闭包,自由变量

    1.变量作用域 先看一个例子 >>> def func(a):      ... print(a)      ... print(b)      ...      >>& ...

  2. 深入理解Python闭包概念

    闭包并不只是一个Python中的概念,在函数式编程语言中应用较为广泛.理解python中的闭包一方面是能够正确的使用闭包,另一方面可以好好体会和思考闭包的设计思想. 1.概念介绍 首先看一下维基上对闭 ...

  3. Python闭包及其作用域

    Python闭包及其作用域 关于Python作用域的知识在python作用域有相应的笔记,这个笔记是关于Python闭包及其作用域的详细的笔记 如果在一个内部函数里,对一个外部作用域(但不是全局作用域 ...

  4. python 闭包_一篇文章读懂Python的闭包与装饰器!

    什么是装饰器? 装饰器(Decorator)相对简单,咱们先介绍它:"装饰器的功能是将被装饰的函数当作参数传递给与装饰器对应的函数(名称相同的函数),并返回包装后的被装饰的函数", ...

  5. Python闭包与装饰器

    Python闭包与装饰器 一.闭包       函数是一个对象,所以可以对象的形式作为某个函数的结果返回.函数执行完后内部变量将会被回收.在闭包中,由于内部函数存在对外部函数的变量的引用,所以即使外部 ...

  6. python闭包的应用场景_简单谈谈Python中的闭包

    Python中的闭包 前几天又有人留言,关于其中一个闭包和re.sub的使用不太清楚.我在脚本之家搜索了下,发现没有写过闭包相关的东西,所以决定总结一下,完善Python的内容. 1. 闭包的概念 首 ...

  7. python 闭包和装饰器详解_实力讲解,一文读懂Python闭包与装饰器!

    什么是装饰器? 装饰器(Decorator)相对简单,咱们先介绍它:"装饰器的功能是将被装饰的函数当作参数传递给与装饰器对应的函数(名称相同的函数),并返回包装后的被装饰的函数", ...

  8. python 闭包和装饰器

    python 闭包和装饰器 一.闭包 闭包:外部函数FunOut()里面包含一个内部函数FunIn(),并且外部函数返回内部函数的对象FunIn,内部函数存在对外部函数的变量的引用.那么这个内部函数F ...

  9. python闭包的应用场景_Python闭包函数定义与用法分析

    python函数的闭包怎么理解小编憎恨离别.但若,离别能让你牵挂,小编愿意离开你. 1. 闭包的概念 首先还得从基本概念说起,什么是闭包呢?来看下维基上的解释: 复制代码代码如下: 在计算机科学中,闭 ...

  10. python生产实战 python 闭包之庖丁解牛篇

    python生产实战 python 闭包之庖丁解牛篇 python生产实战 从闭包到中间件 什么是闭包 计算机中的闭包 数学领域中的闭包 闭包使用场景 C/C++ 中的闭包思想 C 中类似闭包的结构 ...

最新文章

  1. oracle照片字节大小值,Oracle每条记录的平均字节数
  2. 利用最大熵进行阈值分割从而实现灰度图像的二值化的原理概要及OpenCV代码
  3. C# 控制台 模拟时间一秒一秒走动,直到按Esc键,时间静止,退出!
  4. ES6展开运算符(...)
  5. java 注解与反射_Java注解与反射直接上手
  6. 473.火柴拼正方形
  7. 清华大学计算机考研总结,2020考研清华大学计算机考研考试科目总结
  8. 怎样用计算机计算矩阵,【活用工具】教你如何用卡西欧fx82es计算机计算复数 矩阵等...
  9. Js解决微信浏览器刷新的问题
  10. 从零开始学习Java设计模式 | 创建型模式篇:建造者模式
  11. typename的两个意思
  12. UI测试与iTest自动化UI测试工具
  13. 如何创建数仓指标体系?指标建模的基础理论
  14. 利用电脑投放手机声音且可不冲突同时播放电脑声音的方法
  15. 在win7中 很多可用来设置计算机各项,win7系统电脑为什么8g内存只有4g可用?
  16. 苏州新导RFID智能消防仓库管理系统,杭州创思已采用RFID仓库管理系统
  17. Lamdba 和 Stream
  18. 最新县及县以上行政区划代码(截止2013年8月31日)
  19. c遗传算法的终止条件一般_基因遗传算法的终止条件一般是适应度数值小于0.()...
  20. QCC5121 QCC5124 蓝牙5.0芯片 蓝牙音频Soc

热门文章

  1. ORACLE动态sql在存储过程中出现表或视图不存在的解决方法
  2. 系统应用运行WebView闪退
  3. VBA代码实现批量合并同一个文件夹下的所有Excel表
  4. 不懂技术也能看懂的云计算/大数据/人工智能
  5. ubuntu安装360随身wifi驱动
  6. 博弈论sg函数——《移旗子游戏》《剪纸游戏》
  7. 基于JAVA水果销售管理网站计算机毕业设计源码+系统+mysql数据库+lw文档+部署
  8. 雷神一格一体机怎么样?测评值得买吗
  9. 调用百度人脸识别api检测颜值和识别车牌
  10. 第十四届蓝桥杯C++ B组——冶炼金属