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 相关文章推荐
python3编码问题汇总
Sep 06 Python
Tensorflow 查看变量的值方法
Jun 14 Python
Python3爬取英雄联盟英雄皮肤大图实例代码
Nov 14 Python
用Python编写一个高效的端口扫描器的方法
Dec 20 Python
Python并发:多线程与多进程的详解
Jan 24 Python
使用Python的OpenCV模块识别滑动验证码的缺口(推荐)
May 10 Python
pandas.read_csv参数详解(小结)
Jun 21 Python
Python实现的微信红包提醒功能示例
Aug 22 Python
Python从入门到精通之环境搭建教程图解
Sep 26 Python
Pycharm 2020年最新激活码(亲测有效)
Sep 18 Python
Python SQLAlchemy库的使用方法
Oct 13 Python
Python在线和离线安装第三方库的方法
Oct 31 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
Flash空降上海 化身大魔王接受挑战
2020/03/02 星际争霸
php遍历数组的4种方法总结
2014/07/05 PHP
mac os快速切换多个PHP版本的方法
2017/03/07 PHP
javascript 支持ie和firefox杰奇翻页函数
2008/07/22 Javascript
jquery easyui的tabs使用时的问题
2010/03/23 Javascript
IE6/7/8中Option元素未设value时Select将获取空字符串
2011/04/07 Javascript
jquery实现带复选框的表格行选中删除时高亮显示
2013/08/01 Javascript
jQuery.Callbacks()回调函数队列用法详解
2016/06/14 Javascript
jQuery的图片轮播插件PgwSlideshow使用详解
2016/08/11 Javascript
微信小程序 label 组件详解及简单实例
2017/01/10 Javascript
微信小程序 高德地图SDK详解及简单实例(源码下载)
2017/01/11 Javascript
Vue异步组件使用详解
2017/04/08 Javascript
关于bootstrap日期转化,bootstrap-editable的简单使用,bootstrap-fileinput的使用详解
2017/05/12 Javascript
jquery.masonry瀑布流效果
2017/05/25 jQuery
详解如何使用koa实现socket.io官网的例子
2018/11/04 Javascript
Vuex的初探与实战小结
2018/11/26 Javascript
vue axios封装及API统一管理的方法
2019/04/18 Javascript
Openlayers实现地图的基本操作
2020/09/28 Javascript
Django自定义插件实现网站登录验证码功能
2017/04/19 Python
Python3.5编程实现修改IIS WEB.CONFIG的方法示例
2017/08/18 Python
python交互式图形编程实例(一)
2017/11/17 Python
pyftplib中文乱码问题解决方案
2020/01/11 Python
Scrapy框架基本命令与settings.py设置
2020/02/06 Python
Python figure参数及subplot子图绘制代码
2020/04/18 Python
Python中socket网络通信是干嘛的
2020/05/27 Python
Python把图片转化为pdf代码实例
2020/07/28 Python
了解一下python内建模块collections
2020/09/07 Python
python 偷懒技巧——使用 keyboard 录制键盘事件
2020/09/21 Python
NIHAOMARKET官方海外旗舰店:意大利你好华人超市
2018/01/27 全球购物
Carter’s OshKosh加拿大:购买婴幼儿服装和童装
2018/11/27 全球购物
JBL加拿大官方商店:扬声器、耳机等
2020/10/23 全球购物
医学专业应届生的自我评价
2014/02/28 职场文书
2015年试用期自我评价范文
2015/03/10 职场文书
Pycharm远程调试和MySQL数据库授权问题
2022/03/18 MySQL
Redis高可用集群redis-cluster详解
2022/03/20 Redis
SQL中去除重复数据的几种方法汇总(窗口函数对数据去重)
2023/05/08 MySQL