Python编程中运用闭包时所需要注意的一些地方


Posted in Python onMay 02, 2015

写下这篇博客,起源于Tornado邮件群组的这个问题how to use outer variable in inner method,这里面老外的回答很有参考价值,关键点基本都说到了。我在这里用一些有趣的例子来做些解析,简要的阐述下Python的闭包规则,首先看一个经典的例子:

def foo():
 a = 1
 def bar():
  a = a + 1
  # print a + 1
  # b = a + 1
  # a = 1
  print id(a)
 
 bar()
 print a, id(a)

在Python2.x上运行这个函数会报UnboundLocalError: local variable 'a' referenced before assignment即本地变量在引用前未定义,如何来理解这个错误呢?PEP 227里面介绍到,Python解析器在搜索一个变量的定义时是根据如下三级规则来查找的:

    The Python 2.0 definition specifies exactly three namespaces to check for each name — the local namespace, the global namespace, and the builtin namespace.

这里的local实际上可能还有多级,上面的代码就是一个例子,下面通过对代码做些简单的修改来一步步理解这里面的规律:

  •     如果将a = a + 1这句换成print a + 1或者b = a + 1,是不会有问题的,即在内部函数bar内,外部函数foo里的a实际是可见的,可以引用。
  •     将a = a + 1换成 a = 1也是没有问题的,但是如果你将两处出现的a的id打印出来你会发现,其实这两个a不是一回事,在内部函数bar里面,本地的a = 1定义了在bar函数范围内的新的一个局部变量,因为名字和外部函数foo里面的变量a名字相同,导致外部函数foo里的a在内部函数bar里实际已不可见。
  •     再来说a = a + 1出错是怎么回事,首先a = xxx这种形式,Python解析器认为要在内部函数bar内创建一个新的局部变量a,同时外部函数foo里的a在bar里已不可见,而解析器对接下来对右边的a + 1的解析就是用本地的变量a加1,而这时左边的a即本地的变量a还没有创建(等右边赋值呢),因此就这就产生了一个是鸡生蛋还是蛋生鸡的问题,导致了上面说的UnboundLocalError的错误。

要解决这个问题,在Python2.x里主要有两个方案:

    用别名替代比如b = a + 1,内部函数bar内只引用外部函数foo里的a。
    将foo里的a设成一个容器,如list

  

def foo():
  a = [1, ]
  def bar():
   a[0] = a[0] + 1
  
  bar()
  print a[0]

当然这有些时候还是很不方便,因此在Python3.x中引入了一个nonloacal的关键字来解决这个问题,只要在a = a + 1前加一句nonloacal a即可,即显式的指定a不是内部函数bar内的本地变量,这样就可以在bar内正常的使用和再赋值外部函数foo内的变量a了。

在搜索Python闭包相关的材料中,我在StackOverflow上发现一个有趣的有关Python闭包的问题,有兴趣的可以思考思考做做看,结果应该是什么?你预期的结果是什么,若不一致,如果要得到你预期的结果应该怎么改?

flist = []
 
for i in xrange(3):
 def func(x): return x * i
 flist.append(func)
 
for f in flist:
 print f(2)
Python 相关文章推荐
python 多线程应用介绍
Dec 19 Python
Python最长公共子串算法实例
Mar 07 Python
Python 3中print函数的使用方法总结
Aug 08 Python
python使用os.listdir和os.walk获得文件的路径的方法
Dec 16 Python
Python3之简单搭建自带服务器的实例讲解
Jun 04 Python
python list格式数据excel导出方法
Oct 31 Python
Python多项式回归的实现方法
Mar 11 Python
python flask安装和命令详解
Apr 02 Python
python列表每个元素同增同减和列表元素去空格的实例
Jul 20 Python
简单了解python 生成器 列表推导式 生成器表达式
Aug 22 Python
Python爬虫回测股票的实例讲解
Jan 22 Python
Python OpenCV实现图像模板匹配详解
Apr 07 Python
按日期打印Python的Tornado框架中的日志的方法
May 02 #Python
详细解读Python的web.py框架下的application.py模块
May 02 #Python
使用Python的web.py框架实现类似Django的ORM查询的教程
May 02 #Python
在ironpython中利用装饰器执行SQL操作的例子
May 02 #Python
用Python编写简单的定时器的方法
May 02 #Python
用Python程序抓取网页的HTML信息的一个小实例
May 02 #Python
在Mac OS上部署Nginx和FastCGI以及Flask框架的教程
May 02 #Python
You might like
php 小乘法表实现代码
2009/07/16 PHP
php除数取整示例
2014/04/24 PHP
PHP常用正则表达式集锦
2014/08/17 PHP
php写app接口并返回json数据的实例(分享)
2017/05/20 PHP
CheckBox 如何实现全选?
2006/06/23 Javascript
JavaScript 版本自动生成文章摘要
2008/07/23 Javascript
jquery中对于批量deferred的处理方法
2014/01/22 Javascript
JavaScript获取当前页面上的指定对象示例代码
2014/02/28 Javascript
js实现鼠标点击左上角滑动菜单效果代码
2015/09/06 Javascript
学习javascript文件加载优化
2016/02/19 Javascript
JavaScript中的对象继承关系
2016/08/01 Javascript
JS转换HTML转义符的方法
2016/08/24 Javascript
bootstrap侧边栏圆点导航
2017/01/11 Javascript
JS简单获取当前日期和农历日期的方法
2017/04/17 Javascript
以BootStrap Tab为例写一个前端组件
2017/07/25 Javascript
使用D3.js制作图表详解
2017/08/13 Javascript
浅谈Vue Element中Select下拉框选取值的问题
2018/03/01 Javascript
详解React中setState回调函数
2018/06/14 Javascript
js实现下拉框二级联动
2018/12/04 Javascript
新年快乐! javascript实现超级炫酷的3D烟花特效
2019/01/30 Javascript
JavaScript语法约定和程序调试原理解析
2020/11/03 Javascript
Javascript新手入门之字符串拼接与变量的应用
2020/12/03 Javascript
[59:44]2018DOTA2亚洲邀请赛 3.31 小组赛 B组 paiN vs iG
2018/03/31 DOTA
Python中type的构造函数参数含义说明
2015/06/21 Python
django实现登录时候输入密码错误5次锁定用户十分钟
2017/11/05 Python
python2.6.6如何升级到python2.7.14
2018/04/08 Python
Python文件监听工具pyinotify与watchdog实例
2018/10/15 Python
Python使用pyautocad+openpyxl处理cad文件示例
2019/07/11 Python
Python3列表List入门知识附实例
2020/02/09 Python
python Gabor滤波器讲解
2020/10/26 Python
伦敦所有西区剧院演出官方票务代理:Theatre Tickets Direct
2017/05/26 全球购物
2014小学植树节活动总结
2014/03/10 职场文书
投标承诺函格式
2015/01/21 职场文书
2016年教代会开幕词
2016/03/04 职场文书
React 并发功能体验(前端的并发模式)
2021/07/01 Javascript
Spring 使用注解开发
2022/05/20 Java/Android