命名空间,闭包原理,参考点击本文

一、问题描述
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)]

LEGB

只有函数、类、模块会产生作用域,代码块不会产生作用域。作用域按照变量的定义位置可以划分为4类:

Local(函数内部)局部作用域

Enclosing(嵌套函数的外层函数内部)嵌套作用域(闭包)

Global(模块全局)全局作用域

Built-in(内建)内建作用域

python解释器查找变量时,会按照顺序依次查找局部作用域--->嵌套作用域--->全局作用域--->内建作用域,在任意一个作用域中找到变量则停止查找,所有作用域查找完成没有找到对应的变量,则抛出 NameError: name 'xxxx' is not defined的异常。

fun = [lambda x: x*i for i in range(4)] 本质解析/原理,LEGB规则 闭包原理的更多相关文章

  1. 关于 [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] 想要 ...

  2. lambda x:i*x for i in range(4)

    解决方法:冒号前添加接收 i 的变量 return [lambda x,i=i: i * x for i in range(4)]

  3. 背后的故事之 - 快乐的Lambda表达式(一)

    快乐的Lambda表达式(二) 自从Lambda随.NET Framework3.5出现在.NET开发者眼前以来,它已经给我们带来了太多的欣喜.它优雅,对开发者更友好,能提高开发效率,天啊!它还有可能 ...

  4. entity framework 新手入门篇(1.5)-lambda表达式与linq

    在建立好了EF模型之后,先不着急使用它,在使用它之前,你还需要了解两个相关的技术,lambda表达式与linq. 作为微软C#语言中重要的语法糖-lambda表达式与LINQ,本质都是一个方法,以la ...

  5. Python 之 for循环中的lambda

    第一种 f = [lambda x: x*i for i in range(4)]  (如果将x换成i,调用时候就不用传参数,结果都为3) 对于上面的表达式,调用结果: >>> f ...

  6. [翻译]理解Ruby中的blocks,Procs和lambda

    原文出处:Understanding Ruby Blocks, Procs and Lambdas blocks,Procs和lambda(在编程领域被称为闭包)是Ruby中很强大的特性,也是最容易引 ...

  7. Python特殊语法:filter、map、reduce、lambda [转]

    Python特殊语法:filter.map.reduce.lambda [转] python内置了一些非常有趣但非常有用的函数,充分体现了Python的语言魅力! filter(function, s ...

  8. Python filter,map,lambda,reduce,列表解析

    filter用法 filter(func,seq) 将seq的元素逐一代入func,通过func的返回值来判断是保留还是过滤 >>> def foo(x): return x> ...

  9. lambda 的使用汇总

    d=lambda x:x+1print(d(10))lambda 相当于一个轻量函数返回 d=lambda x:x+1 if x>0 else "error"print(d( ...

随机推荐

  1. jQuery -- 光阴似箭(四):jQuery 遍历

    jQuery -- 知识点回顾篇(四):jQuery 遍历 通过 jQuery 遍历,您能够从被选(当前的)元素开始,轻松地在家族树中向上移动(祖先),向下移动(子孙),水平移动(同胞).这种移动被称 ...

  2. redis常用命令及结构

    ##常用结构及命令: keys * #查询所有key randomkey #随机返回key type key #返回key的类型 exists key #判断key是否存在 del key1 key2 ...

  3. python3编写网络爬虫14-动态渲染页面爬取

    一.动态渲染页面爬取 上节课我们了解了Ajax分析和抓取方式,这其实也是JavaScript动态渲染页面的一种情形,通过直接分析Ajax,借助requests和urllib实现数据爬取 但是javaS ...

  4. Automatically migrating data to new machines kafka集群扩充迁移topic

    The partition reassignment tool can be used to move some topics off of the current set of brokers to ...

  5. jar包内的文件导出的注意点

    1.截取文件名 windows 和linux 通用 String fp[] = filePath.replaceAll("\\\\","/").split(&q ...

  6. Spark大数据平台安装教程

    一.Spark介绍 Apache Spark 是专为大规模数据处理而设计的快速通用的计算引擎.Spark是开源的类Hadoop MapReduce的通用并行框架,Spark拥有Hadoop MapRe ...

  7. (1)Python基础

    几种常用类型 int float str bool 基本数值操作 绝对值 abs 四舍五入 round 最大值&最小值

  8. [matlab] 4.M函数

    函数文件的编写 新建一个函数文件 函数的第一行的格式 :function [输出的参数] =函数名 (输入的参数) 输入和输出的参数可以有多个 保存函数文件的时候,注意文件名要和函数名一样 函数头和函 ...

  9. Leetcode:0027

    Leetcode:0027 题目:给定一个数组 nums 和一个值 val,你需要原地移除所有数值等于 val 的元素,返回移除后数组的新长度.不要使用额外的数组空间,你必须在原地修改输入数组并在使用 ...

  10. jenkins+svn+python+appium启动+mail+html报告

    第一步:jenkins从svn中获取最新的测试代码 1.jenkins启动,进入jenkins目录,使用“java -jar jenkins.war”启动(安装后,jenkins已自启动,不用再自己启 ...