Python编程中运用闭包时所需要注意的一些地方

 更新时间:2015年05月02日 16:36:24   投稿:goldensun  
这篇文章主要介绍了Python编程中运用闭包时所需要注意的一些地方,文章来自国内知名的Python开发者felinx的博客,需要的朋友可以参考下

写下这篇博客,起源于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 http接口自动化脚本详解

    python http接口自动化脚本详解

    这篇文章主要为大家详细介绍了python http接口自动化脚本,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2018-01-01
  • 在Python中调用Ping命令,批量IP的方法

    在Python中调用Ping命令,批量IP的方法

    今天小编就为大家分享一篇在Python中调用Ping命令,批量IP的方法,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2019-01-01
  • OpenCV+Python几何变换的实现示例

    OpenCV+Python几何变换的实现示例

    这篇文章主要介绍了OpenCV+Python几何变换的实现示例,图像的几何变换是指将一幅图像映射到另一幅图像内。有缩放、翻转、仿射变换、透视、重映射等操作。感兴趣的可以了解一下
    2021-03-03
  • python中正则表达式findall的用法实例

    python中正则表达式findall的用法实例

    在写着自动化测试的脚本时重新复习了一下正则表达式findall()方法,下面这篇文章主要给大家介绍了关于python中正则表达式findall用法的相关资料,文中通过实例代码介绍的非常详细,需要的朋友可以参考下
    2022-09-09
  • 利用python3筛选excel中特定的行(行值满足某个条件/行值属于某个集合)

    利用python3筛选excel中特定的行(行值满足某个条件/行值属于某个集合)

    这篇文章主要给大家介绍了关于利用python3筛选excel中特定的行(行值满足某个条件/行值属于某个集合)的相关资料,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2020-09-09
  • 基于python 爬虫爬到含空格的url的处理方法

    基于python 爬虫爬到含空格的url的处理方法

    今天小编就为大家分享一篇基于python 爬虫爬到含空格的url的处理方法,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2018-05-05
  • Python 如何将integer转化为罗马数(3999以内)

    Python 如何将integer转化为罗马数(3999以内)

    这篇文章主要介绍了Python 将integer转化为罗马数(3999以内)的操作,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2021-06-06
  • python的变量与赋值详细分析

    python的变量与赋值详细分析

    这篇文章主要介绍了python的变量与赋值详细分析,具有一定参考价值,需要的朋友可以了解下。
    2017-11-11
  • Python实现一键整理百度云盘中重复无用文件

    Python实现一键整理百度云盘中重复无用文件

    有没有头疼过百度云盘都要塞满了,可是又没有工具能剔除大量重复无用的文件?这里教你一个用Python实现的简单方法,通过整理目录的方式来处理我们云盘中无用的文件吧
    2022-08-08
  • python中的opencv 图像分割与提取

    python中的opencv 图像分割与提取

    这篇文章主要介绍了python中的opencv 图像分割与提取,图像中将前景对象作为目标图像分割或者提取出来。对背景本身并无兴趣分水岭算法及GrabCut算法对图像进行分割及提取。具体实现过程需要的朋友可以参考下面文章详细介绍
    2022-06-06

最新评论