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中正则表达式的使用详解
Oct 17 Python
python中引用与复制用法实例分析
Jun 04 Python
深入解析Python中的descriptor描述器的作用及用法
Jun 27 Python
Python中is与==判断的区别
Mar 28 Python
python实现的正则表达式功能入门教程【经典】
Jun 05 Python
python实现对excel进行数据剔除操作实例
Dec 07 Python
python生成带有表格的图片实例
Feb 03 Python
python函数声明和调用定义及原理详解
Dec 02 Python
django连接mysql数据库及建表操作实例详解
Dec 10 Python
Pyspark获取并处理RDD数据代码实例
Mar 27 Python
Python爬虫入门案例之爬取二手房源数据
Oct 16 Python
python单向链表实例详解
May 25 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
一次编写,随处运行
2006/10/09 PHP
说明的比较细的php 正则学习实例
2008/07/30 PHP
PHP 错误之引号中使用变量
2009/05/04 PHP
基于empty函数的判断详解
2013/06/17 PHP
CodeIgniter集成smarty的方法详解
2016/05/26 PHP
Yii2框架RESTful API 格式化响应,授权认证和速率限制三部分详解
2016/11/10 PHP
JSON PHP中,Json字符串反序列化成对象/数组的方法
2018/05/31 PHP
PHPStudy下如何为Apache安装SSL证书的方法步骤
2019/01/23 PHP
自制基于jQuery的智能提示插件一枚
2011/02/18 Javascript
Javascript面向对象编程(三) 非构造函数的继承
2011/08/28 Javascript
jQuery实现点击标题输入详细信息
2013/04/16 Javascript
利用JS延迟加载百度分享代码,提高网页速度
2013/07/01 Javascript
JavaScript获取当前日期是星期几的方法
2015/04/06 Javascript
详解JavaScript函数对象
2015/11/15 Javascript
第二篇Bootstrap起步
2016/06/21 Javascript
微信小程序 websocket 实现SpringMVC+Spring+Mybatis
2017/08/04 Javascript
利用vue和element-ui设置表格内容分页的实例
2018/03/02 Javascript
在Vue环境下利用worker运行interval计时器的步骤
2019/08/01 Javascript
weui中的picker使用js进行动态绑定数据问题
2019/11/06 Javascript
vue 动态创建组件的两种方法
2020/12/31 Vue.js
Python的面向对象思想分析
2015/01/14 Python
python的格式化输出(format,%)实例详解
2018/06/01 Python
pandas.dataframe按行索引表达式选取方法
2018/10/30 Python
python实现批量视频分帧、保存视频帧
2019/05/31 Python
python plotly绘制直方图实例详解
2019/07/22 Python
python 通过手机号识别出对应的微信性别(实例代码)
2019/12/22 Python
详解python定时简单爬取网页新闻存入数据库并发送邮件
2020/11/27 Python
CSS3实现可爱的小黄人动画
2016/07/11 HTML / CSS
Reebok俄罗斯官方网上商店:购买锐步运动服装和鞋子
2016/09/26 全球购物
MAC Cosmetics巴西官方网站:M·A·C彩妆
2019/04/18 全球购物
团日活动总结
2014/04/28 职场文书
积极向上的团队口号
2014/06/06 职场文书
代收款委托书范本
2014/10/01 职场文书
婚前协议书范本
2014/10/27 职场文书
2014小学语文教师个人工作总结
2014/12/03 职场文书
投资公司董事长岗位职责
2015/04/16 职场文书