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内置的字符串处理函数整理
Jan 29 Python
使用python调用浏览器并打开一个网址的例子
Jun 05 Python
分析Python中设计模式之Decorator装饰器模式的要点
Mar 02 Python
Python实现文件复制删除
Apr 19 Python
python实现整数的二进制循环移位
Mar 08 Python
pyqt5 键盘监听按下enter 就登陆的实例
Jun 25 Python
Python基于内置函数type创建新类型
Oct 22 Python
python中四舍五入的正确打开方式
Jan 18 Python
python实现按日期归档文件
Jan 30 Python
python执行js代码的方法
May 13 Python
Python 实现绘制子图及子图刻度的变换等问题
May 31 Python
Python-OpenCV实现图像缺陷检测的实例
Jun 11 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 flush类输出缓冲剖析
2008/10/19 PHP
PHP7新特性foreach 修改示例介绍
2016/08/26 PHP
PHP实现将标点符号正则替换为空格的方法
2017/08/09 PHP
PHP Swoole异步Redis客户端实现方法示例
2019/10/24 PHP
php实现微信和支付宝支付的示例代码
2020/08/11 PHP
Alliance vs AM BO3 第二场2.13
2021/03/10 DOTA
Javascript & DHTML 实例编程(教程)DOM基础和基本API
2007/06/02 Javascript
ECMAScript 创建自己的js类库
2012/11/22 Javascript
js判断undefined变量类型使用typeof
2013/06/03 Javascript
jquery form表单序列化为对象的示例代码
2014/03/05 Javascript
javascript 回调函数详解
2014/11/11 Javascript
nodejs下打包模块archiver详解
2014/12/03 NodeJs
js控制网页前进和后退的方法
2015/06/08 Javascript
js实现当前输入框高亮显示的方法
2015/08/19 Javascript
详解Bootstrap的iCheck插件checkbox和radio
2016/08/24 Javascript
基于JS实现发送短信验证码后的倒计时功能(无视页面刷新,页面关闭不进行倒计时功能)
2016/09/02 Javascript
利用jQuery实现打字机字幕效果实例代码
2016/09/02 Javascript
浅谈js之字面量、对象字面量的访问、关键字in的用法
2016/11/20 Javascript
jQuery事件绑定方法学习总结(推荐)
2016/11/21 Javascript
js 单引号替换成双引号,双引号替换成单引号的实现方法
2017/02/16 Javascript
使用layui前端框架弹出form表单以及提交的示例
2019/10/25 Javascript
react ant Design手动设置表单的值操作
2020/10/31 Javascript
Python基础篇之初识Python必看攻略
2016/06/23 Python
Python合并字典键值并去除重复元素的实例
2016/12/18 Python
对python中使用requests模块参数编码的不同处理方法
2018/05/18 Python
windows下python安装小白入门教程
2018/09/18 Python
vue学习笔记之动态组件和v-once指令简单示例
2020/02/29 Python
移动端解决悬浮层(悬浮header、footer)会遮挡住内容的3种方法
2015/03/27 HTML / CSS
实例讲解使用HTML5 Canvas绘制阴影效果的方法
2016/03/25 HTML / CSS
蔻驰意大利官网:COACH意大利
2019/01/16 全球购物
如何开发安全的AJAX应用
2014/03/26 面试题
职业教育毕业生求职信
2013/11/09 职场文书
公司股东出资证明书
2014/11/01 职场文书
餐厅保洁员岗位职责
2015/04/10 职场文书
深入解读Java三大集合之map list set的用法
2021/11/11 Java/Android
在CSS中使用when/else的方法
2022/01/18 HTML / CSS