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实现系统状态监测和故障转移实例方法
Nov 18 Python
Python中使用异常处理来判断运行的操作系统平台方法
Jan 22 Python
Python的SQLalchemy模块连接与操作MySQL的基础示例
Jul 11 Python
Python入门_浅谈数据结构的4种基本类型
May 16 Python
Python实现的基数排序算法原理与用法实例分析
Nov 23 Python
Python3 加密(hashlib和hmac)模块的实现
Nov 23 Python
用Python实现筛选文件脚本的方法
Oct 27 Python
详解Python 爬取13个旅游城市,告诉你五一大家最爱去哪玩?
May 07 Python
PYQT5 vscode联合操作qtdesigner的方法
Mar 24 Python
Python 执行矩阵与线性代数运算
Aug 01 Python
python 常见的反爬虫策略
Sep 27 Python
只需要100行Python代码就可以实现的贪吃蛇小游戏
May 27 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利用COM对象访问SQLServer、Access
2006/10/09 PHP
PHP脚本的10个技巧(2)
2006/10/09 PHP
php xml文件操作代码(一)
2009/03/20 PHP
php中日期加减法运算实现代码
2011/12/08 PHP
JavaScript 异步调用框架 (Part 5 - 链式实现)
2009/08/04 Javascript
js 获取浏览器高度和宽度值(多浏览器)
2009/09/02 Javascript
键盘 keycode的值 javascript时触发事件时很有用的要素
2009/11/02 Javascript
基于jquery的禁用右键、文本选择功能、复制按键的实现代码
2013/08/27 Javascript
Javascript模拟加速运动与减速运动代码分享
2014/12/11 Javascript
Vue2组件tree实现无限级树形菜单
2017/03/29 Javascript
兼容浏览器的js事件绑定函数(详解)
2017/05/09 Javascript
JavaSctit 利用FileReader和滤镜上传图片预览功能
2017/09/05 Javascript
element-ui 设置菜单栏展开的方法
2018/08/22 Javascript
修改layui的后台模板的左侧导航栏可以伸缩的方法
2019/09/10 Javascript
微信小程序如何获取用户头像和昵称
2019/09/23 Javascript
浅谈v-for 和 v-if 并用时筛选条件方法
2019/11/07 Javascript
Python中处理字符串之isalpha()方法的使用
2015/05/18 Python
python简单贪吃蛇开发
2019/01/28 Python
如何利用Python开发一个简单的猜数字游戏
2019/09/22 Python
Python GUI库PyQt5图形和特效样式QSS介绍
2020/02/25 Python
django日志默认打印request请求信息的方法示例
2020/05/17 Python
python用opencv完成图像分割并进行目标物的提取
2020/05/25 Python
html5嵌入内容_动力节点Java学院整理
2017/07/07 HTML / CSS
Ralph Lauren法国官网:美国高品味时装品牌
2017/12/08 全球购物
乐高瑞士官方商店:LEGO CH
2020/08/16 全球购物
生日宴会答谢词
2014/01/09 职场文书
学生干部学习的自我评价
2014/02/18 职场文书
爱的奉献演讲稿
2014/09/10 职场文书
副乡长民主生活会个人对照检查材料思想汇报
2014/10/01 职场文书
高三英语教学计划
2015/01/23 职场文书
员工自我评价范文
2015/03/11 职场文书
送达通知书
2015/04/25 职场文书
网吧管理制度范本
2015/08/05 职场文书
Nginx进程调度问题详解
2021/09/25 Servers
Vue中使用import进行路由懒加载的原理分析
2022/04/01 Vue.js
Node.js实现爬取网站图片的示例代码
2022/04/04 NodeJs