一个 lambda 表达式引起的思考

一个 lambda表达式 引起的思考

 
 

全文都是抄来的

1. 列表生成式

1.1 range 函数:

 
 

1.2 列表生成式:

 
 

2. 匿名函数: 原文链接

Python中的lambda的“一个语法,三个特性,四个用法,一个争论”。

一个语法

在Python中,lambda的语法是唯一的。其形式如下:

 
 

其中,lambda是Python预留的关键字,argument_list和expression由用户自定义。具体介绍如下。

  1. 这里的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函数的用法扩展为以下几种:

  1. 将lambda函数赋值给一个变量,通过这个变量间接调用该lambda函数。

       
       
  1. 将lambda函数赋值给其他函数,从而将其他函数用该lambda函数替换。

       
       
  2. 将lambda函数作为其他函数的返回值,返回给调用者。

       
       
  1. 将lambda函数作为参数传递给其他函数。
 
 
  1. 另外,部分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的异常。

人生还有意义。那一定是还在找存在的理由

猜你喜欢

转载自www.cnblogs.com/amou/p/9726630.html