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实现扫描指定目录下的子目录及文件的方法
Jul 16 Python
通过Python来使用七牛云存储的方法详解
Aug 07 Python
Python 递归函数详解及实例
Dec 27 Python
用Python写一段用户登录的程序代码
Apr 22 Python
Python常见字典内建函数用法示例
May 14 Python
详解python之协程gevent模块
Jun 14 Python
Python OpenCV处理图像之图像直方图和反向投影
Jul 10 Python
python判断数字是否是超级素数幂
Sep 27 Python
django做form表单的数据验证过程详解
Jul 26 Python
如何解决django-celery启动后迅速关闭
Oct 16 Python
在 Jupyter 中重新导入特定的 Python 文件(场景分析)
Oct 27 Python
Django+python服务器部署与环境部署教程详解
Mar 30 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获取参数的几种方法总结
2014/02/18 PHP
如何在旧的PHP系统中使用PHP 5.3之后的库
2015/12/02 PHP
基于KMP算法JavaScript的实现方法分析
2013/05/03 Javascript
JQueryiframe页面操作父页面中的元素与方法(实例讲解)
2013/11/19 Javascript
利用jQuery简单实现产品展示图片左右滚动功能(示例代码)
2014/01/02 Javascript
js中flexible.js实现淘宝弹性布局方案
2020/06/23 Javascript
JS中如何实现复选框全选功能
2016/12/19 Javascript
vue利用better-scroll实现轮播图与页面滚动详解
2017/10/20 Javascript
jQuery EasyUI 选项卡面板tabs的使用实例讲解
2017/12/25 jQuery
在vue中,v-for的索引index在html中的使用方法
2018/03/06 Javascript
JavaScript设计模式之装饰者模式定义与应用示例
2018/07/25 Javascript
vue项目使用axios发送请求让ajax请求头部携带cookie的方法
2018/09/26 Javascript
jQuery实现的中英文切换功能示例
2019/01/11 jQuery
详解微信小程序入门从这里出发(登录注册、开发工具、文件及结构介绍)
2020/07/21 Javascript
关于angular浏览器兼容性问题的解决方案
2020/07/26 Javascript
js实现特别简单的钟表效果
2020/09/14 Javascript
pycharm 使用心得(八)如何调用另一文件中的函数
2014/06/06 Python
Python解析最简单的验证码
2016/01/07 Python
Pycharm学习教程(5) Python快捷键相关设置
2017/05/03 Python
python 文件转成16进制数组的实例
2018/07/09 Python
Django实现支付宝付款和微信支付的示例代码
2018/07/25 Python
浅谈python编译pyc工程--导包问题解决
2019/03/20 Python
python生成器/yield协程/gevent写简单的图片下载器功能示例
2019/10/28 Python
Django REST framework 单元测试实例解析
2019/11/07 Python
检测tensorflow是否使用gpu进行计算的方式
2020/02/03 Python
python爬虫开发之使用Python爬虫库requests多线程抓取猫眼电影TOP100实例
2020/03/10 Python
Html5踩坑记之mandMobile使用小记
2020/04/02 HTML / CSS
Nobody Denim官网:购买高级女士牛仔裤
2021/03/15 全球购物
什么是重载?CTS、CLS和CLR分别做何解释
2012/05/06 面试题
党员大会主持词
2014/04/02 职场文书
感恩教师节演讲稿
2014/09/03 职场文书
后备干部推荐材料
2014/12/24 职场文书
2015年超市工作总结范文
2015/05/26 职场文书
看雷锋电影观后感
2015/06/10 职场文书
导游词之嵊泗列岛
2019/10/30 职场文书
js不常见操作运算符总结
2021/11/20 Javascript