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 21 Python
Python functools模块学习总结
May 09 Python
详谈python3 numpy-loadtxt的编码问题
Apr 29 Python
Python设计模式之组合模式原理与用法实例分析
Jan 11 Python
python3利用ctypes传入一个字符串类型的列表方法
Feb 12 Python
使用Django简单编写一个XSS平台的方法步骤
Mar 25 Python
Scrapy-Redis结合POST请求获取数据的方法示例
May 07 Python
django 2.2和mysql使用的常见问题
Jul 18 Python
Python中 Global和Nonlocal的用法详解
Jan 20 Python
解决Python pip 自动更新升级失败的问题
Feb 21 Python
Python bytes string相互转换过程解析
Mar 05 Python
Python爬虫:从m3u8文件里提取小视频的正确操作
May 14 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简单提示框alert封装函数
2010/08/08 PHP
PHP开发框架kohana3 自定义路由设置示例
2014/07/14 PHP
php创建桌面快捷方式实现方法
2015/12/31 PHP
PHP开发的文字水印,缩略图,图片水印实现类与用法示例
2019/04/12 PHP
Laravel如何创建服务器提供者实例代码
2019/04/15 PHP
使用Git实现Laravel项目的自动化部署
2019/11/24 PHP
js实现的标题栏新消息闪烁提示效果
2014/06/06 Javascript
javascript实现继承的简单实例
2015/07/26 Javascript
深入浅析NodeJs并发异步的回调处理
2015/12/21 NodeJs
JS延时器提示框的应用实例代码解析
2016/04/27 Javascript
jQuery EasyUI学习教程之datagrid点击列表头排序
2016/07/09 Javascript
AngularJS表单提交实例详解
2017/02/18 Javascript
vue中用动态组件实现选项卡切换效果
2017/03/25 Javascript
Javascript继承机制详解
2017/05/30 Javascript
React Native中的RefreshContorl下拉刷新使用
2017/10/09 Javascript
原生nodejs使用websocket代码分享
2018/04/07 NodeJs
Vue拖拽组件开发实例详解
2018/05/11 Javascript
在vue中使用SockJS实现webSocket通信的过程
2018/08/29 Javascript
JS删除String里某个字符的方法
2021/01/06 Javascript
解决element-ui里的下拉多选框 el-select 时,默认值不可删除问题
2020/08/14 Javascript
react ant Design手动设置表单的值操作
2020/10/31 Javascript
[42:52]IG vs VGJ.T 2018国际邀请赛小组赛BO2 第二场 8.18
2018/08/19 DOTA
python调用新浪微博API项目实践
2014/07/28 Python
Python二分法搜索算法实例分析
2015/05/11 Python
解决PyCharm不运行脚本,而是运行单元测试的问题
2019/01/17 Python
Python基础之文件读取的讲解
2019/02/16 Python
Python常用库Numpy进行矩阵运算详解
2020/07/21 Python
基于Python实现全自动下载抖音视频
2020/11/06 Python
python爬虫使用scrapy注意事项
2020/11/23 Python
HTML5之tabindex属性全面解析
2016/07/07 HTML / CSS
新百伦折扣店:Joe’s New Balance Outlet
2016/08/20 全球购物
应付会计岗位职责
2013/12/12 职场文书
小学感恩节活动总结
2015/03/24 职场文书
企业安全隐患排查治理制度
2015/08/05 职场文书
vue实力踩坑之push当前页无效
2022/04/10 Vue.js
Python函数对象与闭包函数
2022/04/13 Python