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

作者:goldensun 时间:2021-10-27 06:07:21 

写下这篇博客,起源于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
0
投稿

猜你喜欢

  • 如何在ASP中使用SQL存储过程

    2008-02-26 12:09:00
  • python多线程http压力测试脚本

    2022-12-31 16:48:37
  • Python3操作YAML文件格式方法解析

    2021-05-22 03:57:10
  • python 爬取英雄联盟皮肤并下载的示例

    2023-07-22 09:57:45
  • 基于Python手写拼音识别

    2022-10-22 09:24:09
  • python机器学习理论与实战(五)支持向量机

    2021-11-27 11:36:30
  • 关于点击区域

    2009-07-24 13:08:00
  • 一个oracle指令的好网站

    2010-07-21 13:31:00
  • 详解Python安装scrapy的正确姿势

    2023-04-04 01:38:14
  • 一个提高了近10%转化率的改进

    2009-05-22 12:40:00
  • IE9四大渲染引擎模式

    2010-04-20 16:57:00
  • 浏览器根据什么来判定脚本失控?[译]

    2009-02-20 13:36:00
  • 《写给大家看的设计书》阅读笔记之对比原则

    2009-07-15 10:14:00
  • ASP技巧:Script块不能放在另一个Script 块内

    2009-08-19 17:17:00
  • php全局变量和类配合使用深刻理解

    2023-11-18 19:50:17
  • Python 字符串操作实现代码(截取/替换/查找/分割)

    2023-07-14 06:14:00
  • 详解用python写一个抽奖程序

    2023-07-06 10:28:12
  • Python实现为PDF大文件批量去除水印

    2023-04-14 19:59:04
  • 如何使用XML实现多渠道接入网站的构架

    2008-09-05 17:13:00
  • W3C 接连推出 7 个 HTML 草案

    2010-03-10 10:37:00
  • asp之家 网络编程 m.aspxhome.com