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入门者的一些编程建议
Jun 15 Python
Django实现自定义404,500页面教程
Mar 26 Python
详解K-means算法在Python中的实现
Dec 05 Python
Python实现购物评论文本情感分析操作【基于中文文本挖掘库snownlp】
Aug 07 Python
Python实现八皇后问题示例代码
Dec 09 Python
python实现AES加密解密
Mar 28 Python
解决django后台样式丢失,css资源加载失败的问题
Jun 11 Python
Python select及selectors模块概念用法详解
Jun 22 Python
Python如何给函数库增加日志功能
Aug 04 Python
如何解决pycharm调试报错的问题
Aug 06 Python
Python正则re模块使用步骤及原理解析
Aug 18 Python
自定义Django_rest_framework_jwt登陆错误返回的解决
Oct 18 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
深入探讨<br />和 \r\n两者有什么区别??
2013/06/05 PHP
smarty简单入门实例
2014/11/28 PHP
从性能方面考虑PHP下载远程文件的3种方法
2015/12/29 PHP
遍历指定目录,并存储目录内所有文件属性信息的php代码
2016/10/28 PHP
php set_include_path函数设置 include_path 配置选项
2016/10/30 PHP
javascript 对象比较实现代码
2009/04/27 Javascript
JavaScript 函数调用规则
2009/09/14 Javascript
jquery中通过父级查找进行定位示例
2013/06/28 Javascript
JavaScript设计模式开发中组合模式的使用教程
2016/05/18 Javascript
详解如何在 vue 项目里正确地引用 jquery 和 jquery-ui的插件
2017/06/01 jQuery
基于vue2实现上拉加载功能
2017/11/28 Javascript
vue动画之点击按钮往上渐渐显示出来的实例
2018/09/29 Javascript
JavaScript基于数组实现的栈与队列操作示例
2018/12/22 Javascript
使用VUE+iView+.Net Core上传图片的方法示例
2019/01/04 Javascript
vue.js实现的幻灯片功能示例
2019/01/18 Javascript
图文讲解vue的v-if使用方法
2019/02/11 Javascript
vue下使用nginx刷新页面404的问题解决
2019/08/02 Javascript
微信小程序跨页面传递data数据方法解析
2019/12/13 Javascript
[51:32]Optic vs Serenity 2018国际邀请赛淘汰赛BO3 第一场 8.22
2018/08/23 DOTA
一步步教你用Python实现2048小游戏
2017/01/19 Python
python 通过手机号识别出对应的微信性别(实例代码)
2019/12/22 Python
Windows上安装tensorflow  详细教程(图文详解)
2020/02/04 Python
10个python3常用排序算法详细说明与实例(快速排序,冒泡排序,桶排序,基数排序,堆排序,希尔排序,归并排序,计数排序)
2020/03/17 Python
python 爬虫之selenium可视化爬虫的实现
2020/12/04 Python
python绘图模块之利用turtle画图
2021/02/12 Python
video实现有声音自动播放的实现方法
2020/05/20 HTML / CSS
俄罗斯极限运动网上商店:Board Shop №1
2020/12/18 全球购物
如何为DataGridView添加一个定制的Column Type
2014/01/21 面试题
工地门卫岗位职责
2013/12/30 职场文书
精彩自我鉴定
2014/01/16 职场文书
征婚广告词
2014/03/17 职场文书
联片教研活动总结
2014/07/01 职场文书
学院党的群众路线教育实践活动整改方案
2014/10/04 职场文书
2015年世界水日活动总结
2015/02/09 职场文书
2015年新农村建设工作总结
2015/05/22 职场文书
冲出亚马逊观后感
2015/06/03 职场文书