一个 lambda 表达式引起的思考
一个 lambda表达式 引起的思考
全文都是抄来的
1. 列表生成式
1.1 range 函数:
1.2 列表生成式:
2. 匿名函数: 原文链接
Python中的lambda的“一个语法,三个特性,四个用法,一个争论”。
一个语法
在Python中,lambda的语法是唯一的。其形式如下:
其中,lambda是Python预留的关键字,argument_list和expression由用户自定义。具体介绍如下。
- 这里的argument_list是参数列表。它的结构与Python中函数(function)的参数列表是一样的。具体来说,argument_list可以有非常多的形式。例如:
- a, b
- a=1, b=2
- *args
- **kwargs
- a, b=1, *args
- 空
- ......
这里的expression是一个关于参数的表达式。表达式中出现的参数需要在argument_list中有定义,并且表达式只能是单行的。以下都是合法的表达式:
- 1
- None
- a + b
- sum(a)
- 1 if a >10 else 0
- ......
这里的 lambda argument_list: expression
表示的是一个函数。这个函数叫做lambda函数。
三个特性
lambda函数有如下特性:
下面是一些lambda函数示例:
四个用法
由于lambda语法是固定的,其本质上只有一种用法,那就是定义一个lambda函数。在实际中,根据这个lambda函数应用场景的不同,可以将lambda函数的用法扩展为以下几种:
将lambda函数赋值给一个变量,通过这个变量间接调用该lambda函数。
将lambda函数赋值给其他函数,从而将其他函数用该lambda函数替换。
将lambda函数作为其他函数的返回值,返回给调用者。
- 将lambda函数作为参数传递给其他函数。
- 另外,部分Python库函数也接收函数作为参数,例如 gevent 的 spawn 函数。此时,lambda 函数也能够作为参数传入。
3. 闭包 原文链接
1.什么是闭包,闭包必须满足以下3个条件:
- 必须是一个嵌套的函数。
- 闭包必须返回嵌套函数。
- 嵌套函数必须引用一个外部的非全局的局部自由变量。
举个栗子
2.闭包优点
- 避免使用全局变量
- 可以提供部分数据的隐藏
- 可以提供更优雅的面向对象实现
优点1,2 就不说了,很容易理解,关于第三个,例如当在一个类中实现的方法很少时,或者仅有一个方法时,就可以选择使用闭包。
举个栗子
闭包的概念差不多就是这样了。
4. 延迟绑定:
5. 解决问题 原文链接
一、问题描述
上述式子的输出结果: 预计结果为: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: x*i for i in range(4)]
等价于:如下函数
查看该函数命名空间及 I 值变化:
#运行结果为:为了排版美观,我已将输出lambda_函数地址改名为:lam函数1 2 3
可以看见:就像上面所说的:四次循环中外层函数命名空间中的 i 从 0-->1-->2-->3 最后固定为3, 而在此过程中内嵌函数-Lambda函数中因为没有定义 i 所以只有Lambda 函数动态运行时, 在自己命名空间中找不到 i 才去外层函数复制 i = 3 过来,结果就是所有lambda函数的 i 都为 3, 导致得不到预计输出结果:0,1,2,3 只能得到 3, 3, 3, 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
5. LEGB
只有函数、类、模块会产生作用域,代码块不会产生作用域。作用域按照变量的定义位置可以划分为4类:
python解释器查找变量时,会按照顺序依次查找局部作用域--->嵌套作用域--->全局作用域--->内建作用域,在任意一个作用域中找到变量则停止查找,所有作用域查找完成没有找到对应的变量,则抛出 NameError: name 'xxxx' is not defined的异常。
人生还有意义。那一定是还在找存在的理由