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 17 Python
Pandas标记删除重复记录的方法
Apr 08 Python
Python图像滤波处理操作示例【基于ImageFilter类】
Jan 03 Python
python设定并获取socket超时时间的方法
Jan 12 Python
解决Python中定时任务线程无法自动退出的问题
Feb 18 Python
python pandas获取csv指定行 列的操作方法
Jul 12 Python
Python3远程监控程序的实现方法
Jul 15 Python
python 下 CMake 安装配置 OPENCV 4.1.1的方法
Sep 30 Python
详解Python在使用JSON时需要注意的编码问题
Dec 06 Python
Pycharm调试程序技巧小结
Aug 08 Python
Numpy数组的广播机制的实现
Nov 03 Python
在Python中如何使用yield
Jun 07 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
Drupal7中常用的数据库操作实例
2014/03/02 PHP
php使用curl出现Expect:100-continue解决方法
2015/03/03 PHP
通过PHP自带的服务器来查看正则匹配结果的方法
2015/12/24 PHP
验证token、回复图文\文本、推送消息的实用微信类php代码
2016/06/28 PHP
php上传excel表格并获取数据
2017/04/27 PHP
javascript英文日期(有时间)选择器
2007/05/02 Javascript
return false;和e.preventDefault();的区别
2010/07/11 Javascript
浅析JavaScript原型继承的陷阱
2013/12/03 Javascript
Javascript学习指南
2014/12/01 Javascript
JS动态给对象添加事件的简单方法
2016/07/19 Javascript
AngularJS中$http服务常用的应用及参数
2016/08/22 Javascript
浅析JS中对函数function的理解(基础篇)
2016/10/14 Javascript
jQuery Ajax向服务端传递数组参数值的实例代码
2017/09/03 jQuery
详解vue中axios请求的封装
2019/04/08 Javascript
详解express使用vue-router的history踩坑
2019/06/05 Javascript
webpack4从0搭建组件库的实现
2020/11/29 Javascript
Python探索之修改Python搜索路径
2017/10/25 Python
Python实现将数据框数据写入mongodb及mysql数据库的方法
2018/04/02 Python
Python嵌套列表转一维的方法(压平嵌套列表)
2018/07/03 Python
Python OpenCV中的resize()函数的使用
2019/06/20 Python
python实现淘宝购物系统
2019/10/25 Python
pytorch 图像预处理之减去均值,除以方差的实例
2020/01/02 Python
jupyter notebook中新建cell的方法与快捷键操作
2020/04/22 Python
Django模型验证器介绍与源码分析
2020/09/08 Python
python爬虫泛滥的解决方法详解
2020/11/25 Python
Amara美国站:英国高端家居礼品网站,世界各地的奢侈家具品牌
2017/07/26 全球购物
Sneaker Studio波兰:购买运动鞋
2018/04/28 全球购物
英国可持续奢侈品包包品牌:Elvis & Kresse
2018/08/05 全球购物
AURALog面试题软件测试方面
2013/10/22 面试题
如何写出高质量、高性能的MySQL查询
2014/11/17 面试题
施工协议书范本
2014/04/22 职场文书
环保证明
2015/06/23 职场文书
《揠苗助长》教学反思
2016/02/20 职场文书
56句经典英文座右铭
2019/08/09 职场文书
JavaScript文档对象模型DOM
2021/11/20 Javascript
JS实现简单的九宫格抽奖
2022/06/28 Javascript