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中取整的几种方法小结
Jan 06 Python
python matlibplot绘制3D图形
Jul 02 Python
解决Pycharm下面出现No R interpreter defined的问题
Oct 29 Python
python中ImageTk.PhotoImage()不显示图片却不报错问题解决
Dec 06 Python
pandas每次多Sheet写入文件的方法
Dec 10 Python
Python解决线性代数问题之矩阵的初等变换方法
Dec 12 Python
python url 参数修改方法
Dec 26 Python
由Python编写的MySQL管理工具代码实例
Apr 09 Python
Python函数的返回值、匿名函数lambda、filter函数、map函数、reduce函数用法实例分析
Dec 26 Python
Django静态资源部署404问题解决方案
May 11 Python
Python自定义sorted排序实现方法详解
Sep 18 Python
Pycharm学生免费专业版安装教程的方法步骤
Sep 24 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中date与gmdate的区别及默认时区设置
2014/05/12 PHP
JavaScript方法和技巧大全
2006/12/27 Javascript
JavaScript的常见兼容问题及相关解决方法(chrome/IE/firefox)
2013/12/31 Javascript
Javascript实现Web颜色值转换
2015/02/05 Javascript
jquery siblings获取同辈元素用法实例分析
2016/07/25 Javascript
AngularJS 模块详解及简单实例
2016/07/28 Javascript
详解jQuery的表单验证插件--Validation
2016/12/21 Javascript
js自定义弹框插件的封装
2020/08/24 Javascript
详解在vue-cli中使用路由
2017/09/25 Javascript
jQuery 实现左右两侧菜单添加、移除功能
2018/01/02 jQuery
vue非父子组件通信问题及解决方法
2018/06/11 Javascript
Vue.set() this.$set()引发的视图更新思考及注意事项
2018/08/30 Javascript
在node环境下parse Smarty模板的使用示例代码
2019/11/15 Javascript
微信小程序canvas开发水果老虎机的思路详解
2020/02/07 Javascript
Vue自定义全局弹窗组件操作
2020/08/11 Javascript
nestjs中异常过滤器Exceptionfilter的具体使用
2021/02/07 Javascript
[33:23]VG vs Pain 2018国际邀请赛小组赛BO2 第二场 8.18
2018/08/19 DOTA
python判断字符串是否包含子字符串的方法
2015/03/24 Python
Python中在for循环中嵌套使用if和else语句的技巧
2016/06/20 Python
python爬虫系列Selenium定向爬取虎扑篮球图片详解
2017/11/15 Python
Python实现连接postgresql数据库的方法分析
2017/12/27 Python
matplotlib quiver箭图绘制案例
2020/04/17 Python
浅析Python __name__ 是什么
2020/07/07 Python
python如何写个俄罗斯方块
2020/11/06 Python
数据库面试要点基本概念
2013/10/31 面试题
介绍一下SQL中union,intersect和minus
2012/04/05 面试题
swtich是否能作用在byte上,是否能作用在long上,是否能作用在String上
2013/07/06 面试题
暑期培训随笔感言
2014/03/10 职场文书
土建施工员岗位职责
2014/07/16 职场文书
对照四风自我剖析材料
2014/10/07 职场文书
2014年医务科工作总结
2014/12/18 职场文书
2015年文员个人工作总结
2015/04/09 职场文书
建国大业观后感800字
2015/06/01 职场文书
员工聘用合同范本
2015/09/21 职场文书
springboot利用redis、Redisson处理并发问题的操作
2021/06/18 Java/Android
python神经网络Xception模型
2022/05/06 Python