简单讲解Python中的闭包


Posted in Python onAugust 11, 2015

闭包并不是什么新奇的概念,它早在高级语言开始发展的年代就产生了。闭包(Closure)是词法闭包(Lexical Closure)的简称。对闭包的具体定义有很多种说法,这些说法大体可以分为两类:
一种说法认为闭包是符合一定条件的函数,比如参考资源中这样定义闭包:闭包是在其词法上下文中引用了自由变量的函数。
另一种说法认为闭包是由函数和与其相关的引用环境组合而成的实体。比如参考资源中就有这样的的定义:在实现深约束时,需要创建一个能显式表示引用环境的东西,并将它与相关的子程序捆绑在一起,这样捆绑起来的整体被称为闭包。

就像这样:

#python 中的闭包
... def func(data):
...   count = [data]
...   def wrap():
...     count[0] += 1
...     return count[0]
...   return wrap
... 
... a = func(1)
>>> a()
5: 2
>>> a()
6: 3

 def func(x):
...   return lambda y :y+x
>>> b = func(1)
>>> b(1)
7: 2
>>> b(2)
8: 3
>>> print b #这里b是个function 在ruby中是proc
<function <lambda> at 0x01AC68F0>


 def addx(x):
... def adder (y): return x + y
... return adder
>>> add8 = addx(8)
>>> add8(8)
9: 16

简单说,闭包就是根据不同的配置信息得到不同的结果

python实例
看概念总是让人摸不着头脑,看几个python小例子就会了

例1

def make_adder(addend):
  def adder(augend):
    return augend + addend
  return adder

p = make_adder(23)
q = make_adder(44)

print p(100)
print q(100)

运行结果:

123
144

分析一下:
我们发现,make_adder是一个函数,包括一个参数addend,比较特殊的地方是这个函数里面又定义了一个新函数,这个新函数里面的一个变量正好是外部make_adder的参数.也就是说,外部传递过来的addend参数已经和adder函数绑定到一起了,形成了一个新函数,我们可以把addend看做新函数的一个配置信息,配置信息不同,函数的功能就不一样了,也就是能得到定制之后的函数.

再看看运行结果,我们发现,虽然p和q都是make_adder生成的,但是因为配置参数不同,后面再执行相同参数的函数后得到了不同的结果.这就是闭包.

例2

def hellocounter (name):
  count=[0] 
  def counter():
    count[0]+=1
    print 'Hello,',name,',',str(count[0])+' access!'
  return counter

hello = hellocounter('ma6174')
hello()
hello()
hello()

执行结果

Hello, ysisl , 1 access!
Hello, ysisl , 2 access!
Hello, ysisl , 3 access!

分析一下
这个程序比较有趣,我们可以把这个程序看做统计一个函数调用次数的函数.count[0]可以看做一个计数器,没执行一次hello函数,count[0]的值就加1。也许你会有疑问:为什么不直接写count而用一个列表?这是python2的一个bug,如果不用列表的话,会报这样一个错误:

UnboundLocalError: local variable 'count' referenced before assignment.

什么意思?就是说conut这个变量你没有定义就直接引用了,我不知道这是个什么东西,程序就崩溃了.于是,再python3里面,引入了一个关键字:nonlocal,这个关键字是干什么的?就是告诉python程序,我的这个count变量是再外部定义的,你去外面找吧.然后python就去外层函数找,然后就找到了count=0这个定义和赋值,程序就能正常执行了.

python3 代码

def hellocounter (name):
  count=0 
  def counter():
    nonlocal count
    count+=1
    print 'Hello,',name,',',str(count[0])+' access!'
  return counter

hello = hellocounter('ma6174')
hello()
hello()
hello()

例3

def makebold(fn):
  def wrapped():
    return "<b>" + fn() + "</b>"
  return wrapped

def makeitalic(fn):
  def wrapped():
    return "<i>" + fn() + "</i>"
  return wrapped

@makebold
@makeitalic
def hello():
  return "hello world"

print hello()

执行结果

<b><i>hello world</i></b>

简单分析
怎么样?这个程序熟悉吗?这不是传说的的装饰器吗?对,这就是装饰器,其实,装饰器就是一种闭包,我们再回想一下装饰器的概念:对函数(参数,返回值等)进行加工处理,生成一个功能增强版的一个函数。再看看闭包的概念,这个增强版的函数不就是我们配置之后的函数吗?区别在于,装饰器的参数是一个函数或类,专门对类或函数进行加工处理。

python里面的好多高级功能,比如装饰器,生成器,列表推到,闭包,匿名函数等,开发中用一下,可能会达到事半功倍的效果!

Python 相关文章推荐
python字典的常用操作方法小结
May 16 Python
python使用__slots__让你的代码更加节省内存
Sep 05 Python
深入理解Django-Signals信号量
Feb 19 Python
python 列表输出重复值以及对应的角标方法
Jun 11 Python
pandas对dataFrame中某一个列的数据进行处理的方法
Jul 08 Python
python GUI图形化编程wxpython的使用
Jul 19 Python
Python使用正则实现计算字符串算式
Dec 29 Python
Pytorch: 自定义网络层实例
Jan 07 Python
Tensorflow:转置函数 transpose的使用详解
Feb 11 Python
jupyter notebook 多行输出实例
Apr 09 Python
Keras搭建自编码器操作
Jul 03 Python
基于tensorflow权重文件的解读
May 26 Python
Python实现短网址ShortUrl的Hash运算实例讲解
Aug 10 #Python
python实现web方式logview的方法
Aug 10 #Python
python实现JAVA源代码从ANSI到UTF-8的批量转换方法
Aug 10 #Python
python用10行代码实现对黄色图片的检测功能
Aug 10 #Python
详解Python中dict与set的使用
Aug 10 #Python
分析并输出Python代码依赖的库的实现代码
Aug 09 #Python
python根据京东商品url获取产品价格
Aug 09 #Python
You might like
在同一窗体中使用PHP来处理多个提交任务
2006/10/09 PHP
php 抽象类的简单应用
2011/09/06 PHP
PHP中使用strpos函数实现屏蔽敏感关键字功能
2014/08/21 PHP
php实现随机显示图片方法汇总
2015/05/21 PHP
php返回当前日期或者指定日期是周几
2015/05/21 PHP
PHP表单验证内容是否为空的实现代码
2016/11/14 PHP
PHP实现的简单组词算法示例
2018/04/10 PHP
thinkphp5+layui实现的分页样式示例
2019/10/08 PHP
Aster vs KG BO3 第三场2.18
2021/03/10 DOTA
js常用函数 不错
2006/09/08 Javascript
yepnope.js 异步加载资源文件
2011/09/08 Javascript
jquery实现兼容浏览器的图片上传本地预览功能
2013/10/14 Javascript
js二维数组排序的简单示例代码
2014/01/24 Javascript
JS控制网页动态生成任意行列数表格的方法
2015/03/09 Javascript
js获取数组的最后一个元素
2015/04/14 Javascript
js实现图片缓慢放大缩小效果
2016/08/02 Javascript
JavaScript基于DOM操作实现简单的数学运算功能示例
2017/01/16 Javascript
Javascript中prototype与__proto__的关系详解
2018/03/11 Javascript
判断文字超过2行添加展开按钮,未超过则不显示,溢出部分显示省略号
2019/04/28 Javascript
angularjs自定义过滤器demo示例
2019/08/24 Javascript
javascript 易错知识点实例小结
2020/04/25 Javascript
JS+CSS实现过渡特效
2021/01/02 Javascript
[29:23]2014 DOTA2国际邀请赛中国区预选赛 LGD-GAMING VS CIS 第一场1
2014/05/23 DOTA
[01:01:52]完美世界DOTA2联赛PWL S2 GXR vs Magma 第二场 11.25
2020/11/26 DOTA
python实现读取命令行参数的方法
2015/05/22 Python
python用10行代码实现对黄色图片的检测功能
2015/08/10 Python
Python制作词云的方法
2018/01/03 Python
如何基于pythonnet调用halcon脚本
2020/01/20 Python
css3 transform及原生js实现鼠标拖动3D立方体旋转
2016/06/20 HTML / CSS
美国在线家装零售商:Build.com
2016/09/02 全球购物
介绍一下XMLHttpRequest对象
2012/02/12 面试题
JS原生实现轮播图的几种方法
2021/03/23 Javascript
终止劳动合同协议书
2014/10/05 职场文书
家长反馈意见及建议
2015/06/03 职场文书
Win11怎样将锁屏账户头像图片改成动画视频
2021/11/21 数码科技
项目中Nginx多级代理是如何获取客户端的真实IP地址
2022/05/30 Servers