简单讲解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实现下载整个ftp目录的方法
Jan 17 Python
python 打印直角三角形,等边三角形,菱形,正方形的代码
Nov 21 Python
python取代netcat过程分析
Feb 10 Python
Python多继承顺序实例分析
May 26 Python
使用sklearn之LabelEncoder将Label标准化的方法
Jul 11 Python
python学习之hook钩子的原理和使用
Oct 25 Python
python爬虫 基于requests模块发起ajax的get请求实现解析
Aug 20 Python
python 图像处理画一个正弦函数代码实例
Sep 10 Python
Python imread、newaxis用法详解
Nov 04 Python
解决Tensorflow sess.run导致的内存溢出问题
Feb 05 Python
PyTorch中torch.tensor与torch.Tensor的区别详解
May 18 Python
python pymysql链接数据库查询结果转为Dataframe实例
Jun 05 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表单验证的3个函数ISSET()、empty()、is_numeric()的使用方法
2011/08/22 PHP
php和mysql中uft-8中文编码乱码的几种解决办法
2012/04/19 PHP
PHP+Memcache实现wordpress访问总数统计(非插件)
2014/07/04 PHP
学习php设计模式 php实现适配器模式
2015/12/07 PHP
php读取本地json文件的实例
2018/03/07 PHP
PHP中的Iterator迭代对象属性详解
2019/04/12 PHP
js href的用法
2010/05/13 Javascript
IE6弹出“已终止操作”的解决办法
2010/11/27 Javascript
为什么JS中eval处理JSON数据要加括号
2015/04/13 Javascript
JavaScript和JQuery的鼠标mouse事件冒泡处理
2015/06/19 Javascript
使用three.js 画渐变的直线
2016/06/05 Javascript
JavaScript中数组的22种方法必学(推荐)
2016/07/20 Javascript
JavaScript实现点击按钮复制指定区域文本(推荐)
2016/11/25 Javascript
JavaScript之json_动力节点Java学院整理
2017/06/29 Javascript
使用vscode快速建立vue模板过程详解
2019/10/10 Javascript
JS对日期操作封装代码实例
2019/11/08 Javascript
[01:15:45]DOTA2上海特级锦标赛B组小组赛#1 Alliance VS Spirit第一局
2016/02/26 DOTA
[40:55]DOTA2上海特级锦标赛主赛事日 - 2 败者组第二轮#4Newbee VS Fnatic
2016/03/03 DOTA
[49:21]完美世界DOTA2联赛循环赛 Ink Ice vs LBZS BO2第二场 11.05
2020/11/06 DOTA
Python学习笔记_数据排序方法
2014/05/22 Python
Python内置函数OCT详解
2016/11/09 Python
python中通过预先编译正则表达式提高效率
2017/09/25 Python
python机器学习实战之最近邻kNN分类器
2017/12/20 Python
tensorflow实现简单逻辑回归
2018/09/07 Python
windows7 32、64位下python爬虫框架scrapy环境的搭建方法
2018/11/29 Python
python设置随机种子实例讲解
2019/09/12 Python
Python实现图片识别加翻译功能
2019/12/26 Python
python内打印变量之%和f的实例
2020/02/19 Python
PAUL HEWITT手表美国站:德国北部时尚生活配饰品牌,船锚元素
2017/11/18 全球购物
亚洲在线旅行门户网站:Expedia.com.hk(智游网)
2020/04/14 全球购物
我有一个char * 型指针正巧指向一些int 型变量, 我想跳过它们。 为什么如下的代码((int *)p)++; 不行?
2013/05/09 面试题
禁止酒驾标语
2014/06/25 职场文书
纪检干部现实表现材料
2014/08/21 职场文书
债务追讨授权委托书范本
2014/10/16 职场文书
525心理健康活动总结
2015/05/08 职场文书
小学生班干部竞选稿
2015/11/20 职场文书