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的collections模块中namedtuple结构使用示例
Jul 07 Python
flask框架视图函数用法示例
Jul 19 Python
Python提取转移文件夹内所有.jpg文件并查看每一帧的方法
Jun 27 Python
python将字符串list写入excel和txt的实例
Jul 20 Python
使用Python实现文字转语音并生成wav文件的例子
Aug 08 Python
Python类中的装饰器在当前类中的声明与调用详解
Apr 15 Python
Eclipse配置python默认头过程图解
Apr 26 Python
python中导入 train_test_split提示错误的解决
Jun 19 Python
利用Python实现朋友圈中的九宫格图片效果
Sep 03 Python
python super()函数的基本使用
Sep 10 Python
python 爬虫如何正确的使用cookie
Oct 27 Python
python使用smtplib模块发送邮件
Dec 17 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 GD 图像处理组件的常用函数总结
2010/04/28 PHP
PHPMailer发送HTML内容、带附件的邮件实例
2014/07/01 PHP
php文件压缩之PHPZip类用法实例
2015/06/18 PHP
PHP基于单例模式实现的mysql类
2016/01/09 PHP
PHP中的密码加密的解决方案总结
2016/10/26 PHP
php实现生成PDF文件的方法示例【基于FPDF类库】
2018/07/21 PHP
用倒置滤镜把div倒置,再把table倒置。
2007/07/31 Javascript
Kibo 用于处理键盘事件的Javascript工具库
2011/10/28 Javascript
javascript垃圾收集机制与内存泄漏详细解析
2013/11/11 Javascript
JS批量修改PS中图层名称的方法
2014/01/26 Javascript
jquery 实现两级导航菜单附效果图
2014/03/07 Javascript
php结合imgareaselect实现图片裁剪
2015/07/05 Javascript
javascript实现仿百度图片的瀑布流加载效果
2016/04/20 Javascript
JS定时器使用,定时定点,固定时刻,循环执行详解
2016/05/31 Javascript
javascript如何创建对象
2016/08/29 Javascript
JavaScript实现前端实时搜索功能
2020/03/26 Javascript
基于Vue实现关键词实时搜索高亮显示关键词
2018/07/21 Javascript
微信小程序动画(Animation)的实现及执行步骤
2018/10/28 Javascript
vue中使用WX-JSSDK的两种方法(推荐)
2020/01/18 Javascript
Python 中的 else详解
2016/04/23 Python
Python文本相似性计算之编辑距离详解
2016/11/28 Python
python机器学习实战之树回归详解
2017/12/20 Python
VSCode中自动为Python文件添加头部注释
2019/11/14 Python
Python 实现一行输入多个数字(用空格隔开)
2020/04/29 Python
Django中Aggregation聚合的基本使用方法
2020/07/09 Python
如何将Pycharm中调整字体大小的方式设置为"ctrl+鼠标滚轮上下滑"
2020/11/17 Python
python快速安装OpenCV的步骤记录
2021/02/22 Python
CSS3 实现雷达扫描图的示例代码
2020/09/21 HTML / CSS
Html5页面在微信端的分享的实现方法
2018/08/30 HTML / CSS
蹦床仓库:Trampoline Warehouse
2018/12/06 全球购物
化学教师教学反思
2014/01/17 职场文书
校庆标语集锦
2014/06/25 职场文书
健康证明
2015/06/19 职场文书
公司行政管理制度范本
2015/08/05 职场文书
在前女友婚礼上,用Python破解了现场的WIFI还把名称改成了
2021/05/28 Python
MySQL主从切换的超详细步骤
2022/06/28 MySQL