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获取远程图片大小和尺寸的方法
Mar 26 Python
Python实现合并字典的方法
Jul 07 Python
Python的Django框架中消息通知的计数器实现教程
Jun 13 Python
python 垃圾收集机制的实例详解
Aug 20 Python
利用python将pdf输出为txt的实例讲解
Apr 23 Python
python实现电脑自动关机
Jun 20 Python
pycharm修改界面主题颜色的方法
Jan 17 Python
Python虚拟环境venv用法详解
May 25 Python
Pytorch转tflite方式
May 25 Python
Python如何给你的程序做性能测试
Jul 29 Python
请求模块urllib之PYTHON爬虫的基本使用
Apr 08 Python
python数字图像处理实现图像的形变与缩放
Jun 28 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
WIN98下Apache1.3.14+PHP4.0.4的安装
2006/10/09 PHP
PHP 查找字符串常用函数介绍
2012/06/07 PHP
php变量范围介绍
2012/10/15 PHP
php实现的zip文件内容比较类
2014/09/24 PHP
PHP模拟登陆163邮箱发邮件及获取通讯录列表的方法
2015/03/07 PHP
PHP获取一段文本显示点阵宽度和高度的方法
2015/03/12 PHP
Ajax PHP JavaScript MySQL实现简易无刷新在线聊天室
2016/08/17 PHP
记录一次排查PHP脚本执行卡住的问题
2016/12/27 PHP
php设计模式之策略模式应用案例详解
2019/06/17 PHP
Laravel框架中缓存的使用方法分析
2019/09/06 PHP
JavaScript 数组的 uniq 方法
2008/01/23 Javascript
javascript 设置文本框中焦点的位置
2009/11/20 Javascript
基于jquery实现的类似百度搜索的输入框自动完成功能
2011/08/23 Javascript
javascript获取作用在元素上面的样式属性代码
2012/09/20 Javascript
ExtJS4中使用mixins实现多继承示例
2013/12/03 Javascript
jQuery中[attribute]选择器用法实例
2014/12/31 Javascript
JavaScript设置表单上传时文件个数的方法
2015/08/11 Javascript
JS数组合并push与concat区别分析
2015/12/17 Javascript
JS点击缩略图整屏居中放大图片效果
2017/07/04 Javascript
在Vue中使用echarts的方法
2018/02/05 Javascript
vue的diff算法知识点总结
2018/03/29 Javascript
vue-devtools的安装步骤
2018/04/23 Javascript
Vue2 监听属性改变watch的实例代码
2018/08/27 Javascript
简述vue路由打开一个新的窗口的方法
2018/11/29 Javascript
react 生命周期实例分析
2020/05/18 Javascript
python中的多线程实例教程
2014/08/27 Python
python对excel文档去重及求和的实例
2018/04/18 Python
python实现RabbitMQ的消息队列的示例代码
2018/11/08 Python
python语言基本语句用法总结
2019/06/11 Python
Python自动化完成tb喵币任务的操作方法
2019/10/30 Python
python连接打印机实现打印文档、图片、pdf文件等功能
2020/02/07 Python
jQuery treeview树形结构应用
2021/03/24 jQuery
县级文明单位申报材料
2014/05/23 职场文书
学习三严三实心得体会
2014/10/13 职场文书
保留意见审计报告
2015/06/05 职场文书
CSS布局之浮动(float)和定位(position)属性的区别
2021/09/25 HTML / CSS