fun = [lambda x: x*i for i in range(4)] 本质解析/原理,LEGB规则 闭包原理
fun = [lambda x: x*i for i in range(4)]
for item in fun:
print(item(1))
上述式子的输出结果:
预计结果为:0, 2, 4, 6
实际输出为:3, 3, 3, 3
原理:i 在外层作用域
lambda x: x*i为内层(嵌)函数,他的命名空间中只有 {'x': 1} 没有 i ,
所以运行时会向外层函数(这儿是列表解析式函数 [ ])的命名空间中请求 i
而当列表解析式运行时,列表解析式命名空间中的 i 经过循环依次变化为 0-->1-->2-->3 最后固定为 3 ,
所以当lambda x: x*i内层函数运行时,去外层函数取 i 每次都只能取到 3解决办法:变闭包作用域为局部作用域。
给内层函数lambda x: x*i增加参数,命名空间中有了用来存储每次的 i ,
即改成[lambda x, i=i: x*i for i in range(4)]这样每一次,内部循环生成一个lambda 函数时,
都会把 --i--作为默认参数传入lambda的命名空间
循环4次实际lambda表达式为:
第一次:lambda x, i=0 第二次:lambda x, i=1 第三次:lambda x, i=2 第四次:lambda x, i=3
fun = [lambda x, i=i: x*i for i in range(4)]
for item in fun:
print(item(1))
#输出结果为:
0
1
2
3
函数fun = [lambda x: x*i for i in range(4)]等价于:如下函数
def func():
fun_lambda_list = []
for i in range(4):
def lambda_(x):
return x*i
fun_lambda_list.append(lambda_)
return fun_lambda_list
查看该函数命名空间及 I 值变化:
def func():
fun_lambda_list = []
for i in range(4):
def lambda_(x):
print('Lambda函数中 i {} 命名空间为:{}:'.format(i, locals()))
return x*i
fun_lambda_list.append(lambda_)
print('外层函数 I 为:{} 命名空间为:{}'.format(i, locals()))
return fun_lambda_list
fl = func()
fl[0](1)
fl[1](1)
fl[2](1)
fl[3](1)
#运行结果为:为了排版美观,我已将输出lambda_函数地址改名为:lam函数1 2 3
外层函数I为:0 命名空间为:{'i': 0, 'lambda_': lam函数1 'fun_lambda_list': [lam函数1]}
外I:1 命空:{'i': 1, 'lambda_': lam函数2, 'fun_lambda_list': [lam函数1, lam函数2]}
外I:2 命空:{'i': 2, 'lambda_': lam函数3, 'fun_lambda_list': [lam函数1, lam函数2, lam函数3]}
外I:3 命空:{'i': 3, 'lambda_': lam函数4, 'fun_lambda_list': [lam函数1, lam函数2, lam函数3, lam函数4]}
Lambda函数中 i 3 命名空间为:{'i': 3, 'x': 1}:
Lambda函数中 i 3 命名空间为:{'i': 3, 'x': 1}:
Lambda函数中 i 3 命名空间为:{'i': 3, 'x': 1}:
Lambda函数中 i 3 命名空间为:{'i': 3, 'x': 1}:
可以看见:就像上面所说的:四次循环中外层函数命名空间中的 i 从 0-->1-->2-->3 最后固定为3,
而在此过程中内嵌函数-Lambda函数中因为没有定义 i 所以只有Lambda 函数动态运行时,
在自己命名空间中找不到 i 才去外层函数复制 i = 3 过来,结果就是所有lambda函数的 i 都为 3,
导致得不到预计输出结果:0,1,2,3 只能得到 3, 3, 3, 3
- 解决办法:变闭包作用域为局部作用域。
def func():
fun_lambda_list = []
for i in range(4):
def lambda_(x, i= i):
print('Lambda函数中 i {} 命名空间为:{}:'.format(i, locals()))
return x*i
fun_lambda_list.append(lambda_)
return fun_lambda_list
fl = func()
res = []
res.append(fl[0](1))
res.append(fl[1](1))
res.append(fl[2](1))
res.append(fl[3](1))
print(res)
#输出结果为:
Lambda函数中 i 0 命名空间为:{'x': 1, 'i': 0}:
Lambda函数中 i 1 命名空间为:{'x': 1, 'i': 1}:
Lambda函数中 i 2 命名空间为:{'x': 1, 'i': 2}:
Lambda函数中 i 3 命名空间为:{'x': 1, 'i': 3}:
[0, 1, 2, 3]
给内层函数 lambda_增加默认参数,命名空间中有了用来存储每次的 i , 即改成 def lambda_(x, i=i) : 这样每一次,
内部循环生成一个lambda 函数时,都会把 i 作为默认参数传入lambda的命名空间
循环4次实际lambda表达式为:
第一次:lambda_( x, i=0) 第二次:lambda_(x, i=1) 第三次:lambda_(x, i=2) 第四次:lambda_(x, i=3)
这样我们就能得到预计的结果:0, 1, 2, 3
fun = [lambda x,i=i: x*i for i in range(4)]
只有函数、类、模块会产生作用域,代码块不会产生作用域。作用域按照变量的定义位置可以划分为4类:
Local(函数内部)局部作用域
Enclosing(嵌套函数的外层函数内部)嵌套作用域(闭包)
Global(模块全局)全局作用域
Built-in(内建)内建作用域
python解释器查找变量时,会按照顺序依次查找局部作用域--->嵌套作用域--->全局作用域--->内建作用域,在任意一个作用域中找到变量则停止查找,所有作用域查找完成没有找到对应的变量,则抛出 NameError: name 'xxxx' is not defined的异常。
fun = [lambda x: x*i for i in range(4)] 本质解析/原理,LEGB规则 闭包原理的更多相关文章
- 关于 [lambda x: x*i for i in range(4)] 理解
题目: lst = [lambda x: x*i for i in range(4)] res = [m(2) for m in lst] print res 实际输出:[6, 6, 6, 6] 想要 ...
- lambda x:i*x for i in range(4)
解决方法:冒号前添加接收 i 的变量 return [lambda x,i=i: i * x for i in range(4)]
- 背后的故事之 - 快乐的Lambda表达式(一)
快乐的Lambda表达式(二) 自从Lambda随.NET Framework3.5出现在.NET开发者眼前以来,它已经给我们带来了太多的欣喜.它优雅,对开发者更友好,能提高开发效率,天啊!它还有可能 ...
- entity framework 新手入门篇(1.5)-lambda表达式与linq
在建立好了EF模型之后,先不着急使用它,在使用它之前,你还需要了解两个相关的技术,lambda表达式与linq. 作为微软C#语言中重要的语法糖-lambda表达式与LINQ,本质都是一个方法,以la ...
- Python 之 for循环中的lambda
第一种 f = [lambda x: x*i for i in range(4)] (如果将x换成i,调用时候就不用传参数,结果都为3) 对于上面的表达式,调用结果: >>> f ...
- [翻译]理解Ruby中的blocks,Procs和lambda
原文出处:Understanding Ruby Blocks, Procs and Lambdas blocks,Procs和lambda(在编程领域被称为闭包)是Ruby中很强大的特性,也是最容易引 ...
- Python特殊语法:filter、map、reduce、lambda [转]
Python特殊语法:filter.map.reduce.lambda [转] python内置了一些非常有趣但非常有用的函数,充分体现了Python的语言魅力! filter(function, s ...
- Python filter,map,lambda,reduce,列表解析
filter用法 filter(func,seq) 将seq的元素逐一代入func,通过func的返回值来判断是保留还是过滤 >>> def foo(x): return x> ...
- lambda 的使用汇总
d=lambda x:x+1print(d(10))lambda 相当于一个轻量函数返回 d=lambda x:x+1 if x>0 else "error"print(d( ...
随机推荐
- Linux进程优先级的处理--Linux进程的管理与调度(二十二)
1. linux优先级的表示 1.1 优先级的内核表示 linux优先级概述 在用户空间通过nice命令设置进程的静态优先级, 这在内部会调用nice系统调用, 进程的nice值在-20~+19之间. ...
- iOS开发创建UI的耗时操作处理
项目中有网络请求.读写操作等一系列耗时操作时,为了避免阻塞主线程,我们会把这些耗时操作放到子线程中去处理,当处理完成后,再回到主线程更新UI,这样就不会阻塞主线程.但是创建UI的时候一般都是在主线程中 ...
- spyder 快捷键
本文主要介绍了spyder的快捷键. 常用快捷键 快捷键 中文名称 Ctrl+R 替换文本 Ctrl+1 单行注释,单次注释,双次取消注释 Ctrl+4 块注释,单次注释,双次取消注释 F5 运行 ...
- GitHub-暂存区与版本回退
参考博文:廖雪峰Git教程 1. 工作区和暂存区 Git和其他版本控制系统如SVN的一个不同之处就是有暂存区的概念. 1.1. 工作区(Working Directory) 就是你在电脑里能看到的目录 ...
- Teradata的profile使用
1.proflie优势 使用profile可以批量管理用户参数,尤其是在一批用户具有相同的参数配置时,十分便捷. 2.profile可配置用户参数 [Account id][Default datab ...
- grep正则表达式搜索
grep -n -e "INT32 *AdaptorPrmOp" --include "*.c" -r ./ 搜索函数的定义 中间有n个空格
- IE在开发工具启动的情况下(打开F12)时 JS才能执行
在开发一个项目时遇到一个bug:在360急速浏览器的兼容模式下并且是线上环境时js无法执行(360急速浏览器的兼容模式下测试环境就ok), 打开f12以后刷新就没问题了,查了一下网上说的IE6/7是没 ...
- C# 生成和解析二维码
下面是C#和JAVA两个版本的开放源码下载: C#:http://www.codeproject.com/Articles/20574/Open-Source-QRCode-Library JAVA: ...
- JZOJ 5602.【NOI2018模拟3.26】Cti
JZOJ 5602.[NOI2018模拟3.26]Cti Description 有一个 \(n×m\) 的地图,地图上的每一个位置可以是空地,炮塔或是敌人.你需要操纵炮塔消灭敌人. 对于每个炮塔都有 ...
- Md5的生成
1.使用hashlib包(一) import hashlib src = 'anthing' m1 = hash.new() m1.update(src) print (m1.hexdigest()) ...