详解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基础-流程控制
Mar 18 Python
结合Python的SimpleHTTPServer源码来解析socket通信
Jun 27 Python
python实现unicode转中文及转换默认编码的方法
Apr 29 Python
详解python之配置日志的几种方式
May 22 Python
Python可变和不可变、类的私有属性实例分析
May 31 Python
解决py2exe打包后,总是多显示一个DOS黑色窗口的问题
Jun 21 Python
Python多线程爬取豆瓣影评API接口
Oct 22 Python
tensorflow如何继续训练之前保存的模型实例
Jan 21 Python
TensorFlow获取加载模型中的全部张量名称代码
Feb 11 Python
Python如何实现机器人聊天
Sep 10 Python
Django REST Framework 分页(Pagination)详解
Nov 30 Python
opencv读取视频并保存图像的方法
Jun 04 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
[EPIC] Larva vs Flash ZvT @ Crossing Field [2017-10-09]
2020/03/17 星际争霸
深入PHP变量存储的详解
2013/06/13 PHP
浅谈PHP中JSON数据操作
2015/07/01 PHP
JavaScript 学习笔记二 字符串拼接
2010/03/28 Javascript
javascript delete 使用示例代码
2010/03/29 Javascript
javascript中使用css需要注意的地方小结
2010/09/01 Javascript
三种检测iPhone/iPad设备方向的方法
2014/04/23 Javascript
Javascript基础教程之定义和调用函数
2015/01/18 Javascript
Nodejs Express4.x开发框架随手笔记
2015/11/23 NodeJs
jQuery UI库中dialog对话框功能使用全解析
2016/04/23 Javascript
js中scrollTop()方法和scroll()方法用法示例
2016/10/03 Javascript
bootstrap输入框组使用方法
2017/02/07 Javascript
layer弹出层全屏及关闭方法
2018/08/17 Javascript
服务端预渲染之Nuxt(使用篇)
2019/04/08 Javascript
微信小程序如何使用canvas二维码保存至手机相册
2019/07/15 Javascript
jQuery实现聊天对话框
2020/02/08 jQuery
jQuery实现鼠标滑动切换图片
2020/05/27 jQuery
vue导入.md文件的步骤(markdown转HTML)
2020/12/31 Vue.js
Python多线程编程(八):使用Event实现线程间通信
2015/04/05 Python
python中利用Future对象异步返回结果示例代码
2017/09/07 Python
python+matplotlib绘制简单的海豚(顶点和节点的操作)
2018/01/02 Python
TensorFlow模型保存/载入的两种方法
2018/03/08 Python
Python中pip更新和三方插件安装说明
2018/07/08 Python
python lxml中etree的简单应用
2019/05/10 Python
python selenium爬取斗鱼所有直播房间信息过程详解
2019/08/09 Python
python数组循环处理方法
2019/08/26 Python
执行Django数据迁移时报 1091错误及解决方法
2019/10/14 Python
使用Python获取当前工作目录和执行命令的位置
2020/03/09 Python
Python OpenCV实现测量图片物体宽度
2020/05/27 Python
如何用python爬取微博热搜数据并保存
2021/02/20 Python
html5使用canvas画一条线
2014/12/15 HTML / CSS
政风行风评议整改方案
2014/09/15 职场文书
使用golang编写一个并发工作队列
2021/05/08 Golang
java如何实现socket连接方法封装
2021/09/25 Java/Android
php修改word的实例方法
2021/11/17 PHP
详解Go语言中Get/Post请求测试
2022/06/01 Golang