详解Python循环作用域与闭包


Posted in Python onMarch 21, 2019

前言

首先来看一段代码

x_list = [i for i in range(30)]
y_list = [i for i in range(10, 20)]
for y in y_list:
  x_list = filter(lambda a: a != y, x_list)
x_list = list(x_list)
print(x_list)
print(len(x_list))

这段代码会输出什么呢?

正确答案是一个长度为29的List。

[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29]
29

但是实际上,上述代码我们想要表达的意图是从x_list中剔除所有在y_list中的元素。为什么在实际情况下,最终只会剔除一个元素呢?这主要与Python的作用域机制有关。

Python作用域机制

Python与其他语言不同,Python没有循环作用域这个说法。Python的作用域遵循LEGB原则

  1. L, local ? 在lambda函数内或者def函数内部的变量
  2. E, Enclosing-function ? 闭包的作用域
  3. G,Global ? 全局作用域
  4. B, Build-in ? 内建作用域

 为了证明Python没有循环作用域,可以通过下面一段代码验证

for i in range(10):
  pass
print(i)

运行代码,发现可以正常运行,运行结果i==9。由此可以证明Python不存在循环作用域,循环变量属于全局作用域。

基于上述结论,就可以很好地说明为什么上述的filter函数最终只去掉了一个元素。

因为filter函数是一个惰性函数,因此在循环过程中并不会进行实际运算,而当循环完成,需要实际输出的时候,此时全局作用域环境下的i已经变为了一个固定值19,因此最终只有19可以从x_list中去掉。

解决方案——闭包

面对上述问题,我们有两个解决方案。

第一个解决方案——避免惰性求值。可以发现,问题的根源在于filter函数是一个惰性求值函数,因此造成了这个问题。可以通过强制求值运算,强制每一次循环都进行filter操作,从而实现正常的筛选操作。代码如下所示。

x_list = [i for i in range(30)]
y_list = [i for i in range(10, 20)]
for y in y_list:
  x_list = list(filter(lambda a: a != y, x_list))
x_list = list(x_list)
print(x_list)
print(len(x_list))

第二个解决方案——闭包。有时候我们不想放弃惰性求值这个特性,那么我们就需要引入更高级的函数式编程思想——闭包。

因为Python支持函数式编程语法,可以将函数作为变量,因此可以很容易的实现闭包特性。

x_list = [i for i in range(30)]
y_list = [i for i in range(10, 20)]
def check(a, b):
  print('check')
  return a != b
for y in y_list:
  def x_filter(y):
    global x_list
    x_list = filter(lambda x: check(x, y), x_list)
  x_filter(y)
  print('loop')
x_list = list(x_list)
print(x_list)
print(len(x_list))

上面的代码为了证明惰性求值的有效性,因此稍微繁琐了一些。在实际场景中,check函数可以直接写成lambda函数的形式。

闭包之所以能解决循环作用域问题,是因为闭包有独立的作用域。因此即便是惰性求值,但是由于闭包作用于已经将临时变量进行了存储,因此依然可以正确进行筛选操作。

总结

Python与其他编程语言不同,不存在循环临时作用域,因此在某些场景下会出现与其它编程语言结果不一致的BUG。面对这种情况,我们一般可以通过两种方式来解决

1.避免惰性求值
2.使用闭包来保存循环临时变量

以上所述是小编给大家介绍的Python循环作用域与闭包详解整合,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对三水点靠木网站的支持!

Python 相关文章推荐
python多线程编程中的join函数使用心得
Sep 02 Python
Python爬虫实例_城市公交网络站点数据的爬取方法
Jan 10 Python
Python+selenium实现自动循环扔QQ邮箱漂流瓶
May 29 Python
python得到电脑的开机时间方法
Oct 15 Python
Django项目使用CircleCI的方法示例
Jul 14 Python
pandas中DataFrame修改index、columns名的方法示例
Aug 02 Python
Django如何实现网站注册用户邮箱验证功能
Aug 14 Python
python中有帮助函数吗
Jun 19 Python
Python实现中英文全文搜索的示例
Dec 04 Python
python利用xpath爬取网上数据并存储到django模型中
Feb 26 Python
python实战之一步一步教你绘制小猪佩奇
Apr 22 Python
Python IO文件管理的具体使用
Mar 20 Python
浅谈python之高阶函数和匿名函数
Mar 21 #Python
浅谈Python反射 & 单例模式
Mar 21 #Python
详解Python中is和==的区别
Mar 21 #Python
浅谈Python的条件判断语句if/else语句
Mar 21 #Python
python使用thrift教程的方法示例
Mar 21 #Python
在Python中如何传递任意数量的实参的示例代码
Mar 21 #Python
详解python使用turtle库来画一朵花
Mar 21 #Python
You might like
php Session存储到Redis的方法
2013/11/04 PHP
Laravel实现构造函数自动依赖注入的方法
2016/03/16 PHP
PHP页面跳转操作实例分析(header方法)
2016/09/28 PHP
PHP使用ActiveMQ实现消息队列的方法详解
2019/05/31 PHP
javascript的键盘控制事件说明
2008/04/15 Javascript
js和jquery如何获取图片真实的宽度和高度
2014/09/28 Javascript
jQuery中[attribute!=value]选择器用法实例
2014/12/31 Javascript
jquery事件的ready()方法使用详解
2015/11/11 Javascript
Angularjs实现多个页面共享数据的方式
2016/03/29 Javascript
js 提取某()特殊字符串长度的实例
2017/12/06 Javascript
关于vue-router的那些事儿
2018/05/23 Javascript
微信小程序实现天气预报功能
2018/07/18 Javascript
vue封装swiper代码实例解析
2019/10/08 Javascript
vue 导航菜单刷新状态不消失,显示对应的路由界面操作
2020/08/06 Javascript
react的hooks的用法详解
2020/10/12 Javascript
[01:07:11]Secret vs Newbee 2019国际邀请赛小组赛 BO2 第二场 8.15
2019/08/17 DOTA
Python中django学习心得
2017/12/06 Python
Python实现的拉格朗日插值法示例
2019/01/08 Python
几行Python代码爬取3000+上市公司的信息
2019/01/24 Python
为什么从Python 3.6开始字典有序并效率更高
2019/07/15 Python
python3字符串操作总结
2019/07/24 Python
pandas 缺失值与空值处理的实现方法
2019/10/12 Python
Python利用for循环打印星号三角形的案例
2020/04/12 Python
Python 实现 T00ls 自动签到脚本代码(邮件+钉钉通知)
2020/07/06 Python
python中的django是做什么的
2020/07/31 Python
python 实现客户端与服务端的通信
2020/12/23 Python
基于HTML5 Canvas:字符串,路径,背景,图片的详解
2013/05/09 HTML / CSS
HTML5通过调用canvas对象的getContext()方法来获取绘图环境
2014/06/23 HTML / CSS
关于赌博的检讨书
2014/01/08 职场文书
计算机专业毕业生自荐书
2014/06/02 职场文书
大学生在校表现评语
2014/12/31 职场文书
银行客户经理岗位职责
2015/04/09 职场文书
傲慢与偏见电影观后感
2015/06/10 职场文书
2016高一新生军训心得体会
2016/01/11 职场文书
导游词之澳门妈祖庙
2019/12/19 职场文书
《总之就是很可爱》新作短篇动画《总之就是很可爱~制服~》将于2022年夏天播出
2022/04/07 日漫