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 Queue模块详解
Nov 30 Python
Python实现简单截取中文字符串的方法
Jun 15 Python
python Django批量导入不重复数据
Mar 25 Python
Python实现类的创建与使用方法示例
Jul 25 Python
在matplotlib的图中设置中文标签的方法
Dec 13 Python
python 二维数组90度旋转的方法
Jan 28 Python
Python面向对象程序设计多继承和多态用法示例
Apr 08 Python
python实现井字棋小游戏
Mar 04 Python
python如何使用代码运行助手
Jul 03 Python
opencv 图像轮廓的实现示例
Jul 08 Python
Python图像处理之图像拼接
Apr 28 Python
python 中的@运算符使用
May 26 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实现对图片的反色处理功能【测试可用】
2018/02/01 PHP
PHP开发实现快递查询功能详解
2019/04/08 PHP
Laravel框架路由管理简单示例
2019/05/07 PHP
laravel 修改记住我功能的cookie保存时间的方法
2019/10/14 PHP
javascript 日期时间函数(经典+完善+实用)
2009/05/27 Javascript
jquery 关键字“拖曳搜索”之“拖曳”以及 图片“提示自适应放大”效果 的实现
2010/04/18 Javascript
JavaScript的Module模式编程深入分析
2013/08/13 Javascript
Extjs grid panel自带滚动条失效的解决方法
2014/09/11 Javascript
nodejs教程之环境安装及运行
2014/11/21 NodeJs
JS获取网页图片name属性的方法
2015/04/01 Javascript
基于javascript实现浏览器滚动条快到底部时自动加载数据
2015/11/30 Javascript
javascript如何创建对象
2016/08/29 Javascript
JS ES6中setTimeout函数的执行上下文示例
2017/04/27 Javascript
jQuery 添加样式属性的优先级别方法(推荐)
2017/06/08 jQuery
实例讲解DataTables固定表格宽度(设置横向滚动条)
2017/07/11 Javascript
在vue中使用css modules替代scroped的方法
2018/03/10 Javascript
详解vue 计算属性与方法跟侦听器区别(面试考点)
2018/04/23 Javascript
详解如何构建Promise队列实现异步函数顺序执行
2018/10/23 Javascript
JQuery获取元素尺寸、位置及页面滚动事件应用示例
2019/05/14 jQuery
python自然语言编码转换模块codecs介绍
2015/04/08 Python
python操作sqlite的CRUD实例分析
2015/05/08 Python
浅谈Tensorflow模型的保存与恢复加载
2018/04/26 Python
python图像处理入门(一)
2019/04/04 Python
深入了解Python装饰器的高级用法
2020/08/13 Python
理解Django 中Call Stack机制的小Demo
2020/09/01 Python
pyqt5实现井字棋的示例代码
2020/12/07 Python
巴西电子产品购物网站:Saldão da Informática
2018/01/09 全球购物
链表面试题-一个链表的结点结构
2015/05/04 面试题
进程的查看和调度分别使用什么命令
2015/03/25 面试题
用Java语言将一个键盘输入的数字转化成中文输出
2013/01/25 面试题
皮肤科医师岗位职责
2013/12/04 职场文书
乡镇挂职心得体会
2014/09/04 职场文书
二人合伙经营协议书
2014/09/13 职场文书
党员专题组织生活会发言材料
2014/10/17 职场文书
教研活动主持词
2015/07/03 职场文书
提档介绍信范文
2015/10/22 职场文书