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通过pil模块获得图片exif信息的方法
Mar 16 Python
python获取目录下所有文件的方法
Jun 01 Python
Django框架下在视图中使用模版的方法
Jul 16 Python
python读取TXT每行,并存到LIST中的方法
Oct 26 Python
Opencv-Python图像透视变换cv2.warpPerspective的示例
Apr 11 Python
基于python plotly交互式图表大全
Dec 07 Python
在Tensorflow中实现梯度下降法更新参数值
Jan 23 Python
tensorflow 限制显存大小的实现
Feb 03 Python
python实现连连看游戏
Feb 14 Python
Python使用struct处理二进制(pack和unpack用法)
Nov 12 Python
详解Java中一维、二维数组在内存中的结构
Feb 11 Python
Python实现仓库管理系统
May 30 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与Java进行通信的实现方法
2013/10/21 PHP
php获取客户端IP及URL的方法示例
2017/02/03 PHP
PHP实现的简单组词算法示例
2018/04/10 PHP
baidu博客的编辑友情链接的新的层窗口!经典~支持【FF】
2007/02/09 Javascript
Jquery UI震动效果实现原理及步骤
2013/02/04 Javascript
商城常用滚动的焦点图效果代码简单实用
2013/03/28 Javascript
分享一个常用的javascript静态类
2014/12/31 Javascript
JavaScript获取元素尺寸和大小操作总结
2015/02/27 Javascript
js判断手机号运营商的方法
2015/10/23 Javascript
js实现的星星评分功能函数
2015/12/09 Javascript
全面解析多种Bootstrap图片轮播效果
2016/05/27 Javascript
JavaScript中的操作符类型转换示例总结
2016/05/30 Javascript
JS中常用的正则表达式
2016/09/29 Javascript
ES6概念 ymbol.for()方法
2016/12/25 Javascript
vue 1.x 交互实现仿百度下拉列表示例
2017/10/21 Javascript
Vue + Vue-router 同名路由切换数据不更新的方法
2017/11/20 Javascript
使用vue-router设置每个页面的title方法
2018/02/11 Javascript
ES6 fetch函数与后台交互实现
2018/11/14 Javascript
详解使用webpack+electron+reactJs开发windows桌面应用
2019/02/01 Javascript
Node 使用express-http-proxy 做api网关的实现
2020/10/15 Javascript
py中的目录与文件判别代码
2008/07/16 Python
下载安装setuptool和pip linux安装pip    
2014/01/24 Python
用Python逐行分析文件方法
2019/01/28 Python
python3.6根据m3u8下载mp4视频
2019/06/17 Python
numpy.meshgrid()理解(小结)
2019/08/01 Python
使用Pandas将inf, nan转化成特定的值
2019/12/19 Python
pyautogui自动化控制鼠标和键盘操作的步骤
2020/04/01 Python
Pytorch 使用 nii数据做输入数据的操作
2020/05/26 Python
The North Face北面美国官网:美国著名户外品牌
2018/09/15 全球购物
英国历史最悠久的DJ设备供应商:DJ Finance、DJ Warehouse、The DJ Shop
2019/09/04 全球购物
商务英语应届生自我鉴定
2013/12/08 职场文书
五好家庭事迹材料
2014/12/20 职场文书
介绍信格式
2015/01/30 职场文书
2016年大学生就业指导课心得体会
2015/10/09 职场文书
2016元旦文艺汇演主持词(开场白+结束语)
2015/12/03 职场文书
Nginx 安装SSL证书完成HTTPS部署
2022/04/28 Servers