简单讲解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中的并发编程实例
Jul 07 Python
Python使用Supervisor来管理进程的方法
May 28 Python
详解Python发送邮件实例
Jan 10 Python
Python lambda函数基本用法实例分析
Mar 16 Python
python 实现A*算法的示例代码
Aug 13 Python
python 把列表转化为字符串的方法
Oct 23 Python
对python 中re.sub,replace(),strip()的区别详解
Jul 22 Python
django使用haystack调用Elasticsearch实现索引搜索
Jul 24 Python
Python动态导入模块:__import__、importlib、动态导入的使用场景实例分析
Mar 30 Python
Jupyter notebook如何实现指定浏览器打开
May 13 Python
python/golang 删除链表中的元素
Sep 14 Python
python scipy 稀疏矩阵的使用说明
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
Amazon Prime Video平台《无限住人 -IMMORTAL-》2020年开始TV放送!
2020/03/06 日漫
文件系统基本操作类
2006/11/23 PHP
PHP面向对象编程之深入理解方法重载与方法覆盖(多态)
2015/12/24 PHP
js基于qrcode.js生成二维码的方法【附demo插件源码下载】
2016/12/28 PHP
浅谈PHP中的面向对象OOP中的魔术方法
2017/06/12 PHP
javascript学习笔记(七) js函数介绍
2012/06/19 Javascript
seajs1.3.0源码解析之module依赖有序加载
2012/11/07 Javascript
JS实现点击文字对应DIV层不停闪动效果的方法
2015/03/02 Javascript
JavaScript点击按钮后弹出透明浮动层的方法
2015/05/11 Javascript
WebGL利用FBO完成立方体贴图效果完整实例(附demo源码下载)
2016/01/26 Javascript
关于Javascript中defer和async的区别总结
2016/09/20 Javascript
ajax的分页查询示例(不刷新页面)
2017/01/11 Javascript
详解AngularJS1.6版本中ui-router路由中/#!/的解决方法
2017/05/22 Javascript
使用原生js+canvas实现模拟心电图的实例
2017/09/20 Javascript
ztree实现左边动态生成树右边为内容详情功能
2017/11/03 Javascript
vue通过点击事件读取音频文件的方法
2018/05/30 Javascript
小程序实现带年月选取效果的日历
2018/06/27 Javascript
layer弹出的iframe层在执行完毕后关闭当前弹出层的方法
2018/08/17 Javascript
浅谈webpack+react多页面开发终极架构
2018/11/11 Javascript
Tornado服务器中绑定域名、虚拟主机的方法
2014/08/22 Python
Python使用MYSQLDB实现从数据库中导出XML文件的方法
2015/05/11 Python
Python3如何解决字符编码问题详解
2017/04/23 Python
用python生成1000个txt文件的方法
2018/10/25 Python
这可能是最好玩的python GUI入门实例(推荐)
2019/07/19 Python
解决python有时候import不了当前的包问题
2019/08/28 Python
django中的数据库迁移的实现
2020/03/16 Python
python3.9实现pyinstaller打包python文件成exe
2020/12/13 Python
详解如何在css3打包后自动追加前缀插件:autoprefixer
2018/12/18 HTML / CSS
如何在发生故障的节点上重新安装 SQL Server
2013/03/14 面试题
《九寨沟》教学反思
2014/04/08 职场文书
2016大学自主招生推荐信范文
2015/03/23 职场文书
小学中队委竞选稿
2015/11/20 职场文书
go语言中切片与内存复制 memcpy 的实现操作
2021/04/27 Golang
Python实战之用tkinter库做一个鼠标模拟点击器
2021/04/27 Python
原生JavaScript实现简单五子棋游戏
2021/06/28 Javascript
深入解析Apache Hudi内核文件标记机制
2022/03/31 Servers