除了def语句之外,Python还提供了一种生成函数对象的表达式形式。由于它与LISP语言中的一个工具很相似,所以称为lambda。就像def一样,这个表达式创建了一个之后能够调用的函数,但是它返回了一个函数而不是将这个函数赋值给一个变量名。这也就是lambda有时叫做匿名函数的原因。实际上,他们常常以一种行内进行函数定义的形式使用,或者用作推迟执行一些代码。

lambda表达式

lambda的一般形式是关键字lambda,之后是一个或多个参数(与一个def头部内用括号括起来的参数列表及其相似),紧跟的是一个冒号,之后是一个表达式:

lambda argument1,argument2,... argumentN:expression using argument

 

由lambda表达式所返回的函数对象与由def创建并复制后的函数对象工作起来是完全一样的,但是lambda由一些不同之处让其在扮演特定的角色时很有用。

  • lambda是一个表达式,而不是一个语句。因为这一点,lambda能够出现在Python语法不允许def出现的地方——例如,在一个列表常量中或者函数调用的参数中。此外,作为一个表达式,lambda返回了一个值(一个新的函数),可以选择性的赋值给一个变量名。相反,def语句总是得在头部将一个新的函数赋值给一个变量名,而不是讲这个函数作为结果返回。

  • lambda的主体是一个单个的表达式,而不是一个代码块。这个lambda的主体简单得就好像放在def主体的return语句中的代码一样。简单地将结果写成一个顺畅的表达式,而不是明确的返回。因为它仅限于表达式,lambda通常要比def功能要小:你仅能够在lambda主体中封装有限的逻辑进去,连if这样的语句都不能够使用。 这是有意设计的——它限制了程序的嵌套:lambda是一个为编写简单的函数而设计的,而def用来处理更大的任务。

除了这些差别,def 和 lambda都能够做同样种类的工作。例如,我们见到了如何使用def语句创建函数。

# ###################### 普通函数 ######################
# 定义函数(普通方式)
def func(arg):
return arg + 1 # 执行函数
result = func(123)

但是,能够使用lambda表达式达到相同的效果,通过明确地将结果赋值给一个变量名,之后就能够通过这个变量名调用这个函数。

# ###################### lambda ######################

# 定义函数(lambda表达式)
my_lambda = lambda arg : arg + 1 # 执行函数
result = my_lambda(123)

这里的f被赋值给一个lambda表达式创建的函数对象。 这也就是def所完成的任务,只不过def的赋值是自动进行的。

默认参数也能够在lambda参数中使用,就像在def中使用一样。

>>> x = (lambda a="fee", b="fie", c="foe": a + b + c)
>>> x("wee")
'weefiefoe'

在lambda主体中的代码想在def内的代码一样都遵循相同的作用于查找法则。lambda表达式引入的一个本地作用域更像一个嵌套的def语句,将会自动从上层函数中、模块中 以及内置作用域中(通过LEGB法则)查找变量名。

>>> def knights():
... title = "Sir"
... action = (lambda x: title + ' ' + x)
... return action
...
>>> act = knight()
>>> act('robin')
'Sir robin'

在Python 2中,变量名title的值通常会修改为通过默认参数的值传入。

为什么使用lambda

通常来说,lambda起到了一种函数速写的作用,允许在使用的代码内嵌入一个函数的定义。他们完全是可选的(你总是能够使用def来替代它们),但是你仅需要嵌入小段可执行代码的情况下它们会带来一个更简洁的代码结构。

例如,我们在稍后会看到回调处理器,它常常在一个注册调用(registration call)的参数列表中编写成单行的lambda表达式,而不是使用在文件其他地方的一个def来定义,之后引用那个变量名。

lambda通常用来编写跳转表(jump table),也就是行为的列表或字典,能够按照需要执行相应的动作。如下段代码所示。

L = [lambda x: x**2,
lambda x: x**3,
lambda x: x**4] for f in L:
print(f(2)) # prints 4, 8, 16 print(L[0](3)) # prints 9

当需要把小段的可执行代码编写进def语句从语法上不能编写进的地方时,lambda表达式作为def的一种速写来说是最为有用的。 例如,这种代码片段,可以通过在列表常量中嵌入lambda表达式创建一个含有三个函数的列表。一个def是不会再列表常量中工作的,因为它是一个语句,而不是一个表达式。 对等的def代码可能需要在想要使用的环境之外有临时性的函数名称和函数定义。

def f1(x): return x**2
def f2(x): return x**3
def f3(x): return x**4 L = [f1, f2, f3] for f in L:
print(f(2)) # prints 4, 8, 16 print(L[0](3)) # prints 9

实际上,我们可以使用Python中的字典或者其他的数据结构来构建更多种类的行为表,从而做同样的事情。 下面是以交互提示模式给出的另一个例子:

>>> key = 'got'
>>> {'already': (lambda: 2 + 2),
... 'got': (lambda: 2 * 4),
... 'one': (lambda: 2 ** 6)}[key]()
8

这里,当Python常见这个字典的时候,每个嵌套的lambda都生成并留下了一个在之后能够调用的函数。通过键索引来取回其中一个函数,而括号使去除的函数被调用。 与在之前向你展示的if语句的扩展用法相比,这样编写代码可以使字典成为更加通用的多路分支工具。

如果不是用lambda做这种工作,需要使用三个文件中其他地方出现过的def语句来替代,也就是在这些函数将会使用的那个字典外的某处需要定义这些函数。

>>> def f1(): return 2 + 2
...
>>> def f2(): return 2 * 4
...
>>> def f3(): return 2 ** 6
...
>>> key = 'one'
>>> {'already': f1, 'got': f2, 'one': f3}[key]()
64

同样,会实现相同的功能,但是def也许会出现在文件中的任意位置,即使它们之后很少的代码。类似刚才lambda的代码,提供了一种特别有用的可以在单个情况出现的函数:如果这里的三个函数不会在其他的地方使用到,那么将它们定义作为lambda嵌入在字典中就很合理了。不仅如此,def格式要求为这些小函数创建变量名,这些变量名也许会与这个文件中的其他变量名发生冲突(也可能不会,但总是可能的)。

如何(不要)让Python代码变得晦涩难懂

由于lambda的主体必须是个表达式(而不是一些语句),由此可见仅能将有限的逻辑封装到一个lambda中。如果你知道在做什么,那么你就能在Python中作为基于表达式等效的写法编写足够多的语句。

例如,如果你希望在lambda函数中进行print,直接编写sys.stdout.write(str(x) + "\n") 这个表达式,而不是使用print(x)这样的语句。 类似地,要在一个lambda中潜逃逻辑,可以使用if/else三元表达式,或者对等的但需要些技巧的and/or组合。正如我们前面所了解到的,如下语句:

if a:
b
else:
c

能够由以下的概括等效的表达式来模拟:

b if a else c
((a and b) or c)

因为这样类似的表达式能够放在lambda中,所以它们能够在lambda函数中来实现选择逻辑。

>>> lower = (lambda x, y: x if x < y else y)
>>> lower('bb', 'aa')
'aa'
>>> lower('aa', 'bb')
'aa'

此外,如果需要在lambda函数中执行循环,能够嵌入map调用或列表解析表达式这样的工具来实现。

>>> import sys
>>> showall = lambda x: list(map(sys.stdout.write, x)) >>> t = showall(['spam\n', 'toast\n', 'eggs\n'])
spam
toast
eggs >>> showall = lambda x: [sys.stdout.write(line) for line in x] # 列表解析
>>> t = showall(['spam\n', 'toast\n', 'eggs\n'])
spam
toast
eggs

这些技巧必须在万不得已的情况下才使用。一不小心,它们就会导致不可读(也成为晦涩难懂)的Python代码。 一般来说,简洁优于复杂,明确优于晦涩,而且一个完整的语句要比神秘的表达式要好。 这就是为什么lambda仅限于表达式。如果你有更负责的代码要编写,可使用def,  lambda针对较小的一段内联代码。 从另一个方面来说,你也会发现湿度的使用这些技术是很有用处的。

嵌套lambda和作用域

lambda是嵌套函数作用域查找(LEGB原则中的E)的最大受益者。 例如,在下面的例子中,lambda出现在def中(很典型的情况),并且在商城函数调用的时候,嵌套的lambda能够获取到上层函数作用域中的变量名x的值。

>>> def action(x):
return (lambda y: x + y) >>> act = action(99)
>>> act
<function action.<locals>.<lambda> at 0x0000014EF59F4C80>
>>> act(2)
101

在之前讲关于嵌套函数作用域的讨论没有标明的就是lambda也能够获取任意上层lambda中的变量名。 这种情况有些隐晦,但是想象一下,如果我们上一个例子中高端def换成一个lambda。

>>> action = (lambda x:(lambda y: x + y))
>>> act = action(99)
>>> act(3)
102
>>> ((lambda x: (lambda y: x + y))(99))(4)
103

这里嵌套的lambda结构让函数在调用时创建了一个函数。无论以上那种情况,嵌套的lambda代码都能够获取上层lambda函数中的变量x。这可以工作,但是这种代码让人相当费解。处于可读性的要求,通常来说,最好避免使用嵌套的lambda。

【Python学习笔记之三】lambda表达式用法小结的更多相关文章

  1. Python学习笔记:lambda表达式

    lambda表达式:通常是在需要一个函数,但又不想去命名一个函数的时候使用,即匿名函数. 示例如下: add = lambda x,y : x+ y add(1,2) # 结果为3 1.应用在函数式编 ...

  2. 0028 Java学习笔记-面向对象-Lambda表达式

    匿名内部类与Lambda表达式示例 下面代码来源于:0027 Java学习笔记-面向对象-(非静态.静态.局部.匿名)内部类 package testpack; public class Test1{ ...

  3. Python学习笔记——部分常用/特殊用法

    1.使用*号来展开序列,*是序列展开,每个元素都当做一个参数.ls = (1, 2, 3);foo(ls),这样foo只有一个参数,就是ls这个列表本身foo(*ls), foo得到3个参数,分别为1 ...

  4. 高放的c++学习笔记之lambda表达式

    lambda表达式:可以让代码看起来更整洁,有些结构简单且用的次数少的函数可以用lambda表达式替代, 通常结构是这样的[捕获列表](参数列表){函数部分} 捕获列表: lambda表达式如果在一个 ...

  5. Java学习笔记--Java8 Lambda表达式

    Java Lambda表达式入门:http://blog.csdn.net/renfufei/article/details/24600507 lambda内容的介绍:http://swiftlet. ...

  6. java8学习笔记之lambda表达式

    1.lambda表达式特点 lambda表达式可以理解为可传递的匿名函数的一种方式,无名称,但有参数列表和函数体以及返回类型,可能还有一个可抛出异常的列表. 2.lambda表达式基本语法 (para ...

  7. Java8新特性学习笔记(一) Lambda表达式

    没有用Lambda表达式的写法: Comparator<Transaction> byYear = new Comparator<Transaction>() { @Overr ...

  8. Java 学习笔记(11)——lambda 表达式

    在写Java代码的时候,如果某个地方需要一个接口的实现类,一般的做法是新定义一个实现类,并重写接口中的方法,在需要使用的时候new一个实现类对象使用,为了一个简单的接口或者说为了一个回调函数就得额外编 ...

  9. [Python学习笔记-002] lambda, map, filter and reduce

    1. lambda lambda, 即匿名函数,可以理解为跟C语言的宏类似.例如: >>> max = lambda x, y: x if x > y else y >& ...

随机推荐

  1. oracle 数据库数据备份

    oracle 数据库数据备份 1.使用oracle用户应该就可以进行数据备份(不需要root用户):su oracle 查oracle实例名:echo $ORACLE_SID       例如查出来的 ...

  2. November 16th 2016 Week 47th Wednesday

    Success is falling nine times and getting up ten. 成功就是哪怕跌倒九次,也要在第十次爬起来. For most of us, we may choos ...

  3. EF 实体类的制定属性不生成数据库字段

    添加一个标签即可 [NotMapped] 没什么营养,就是防忘记

  4. Jmeter入门3 http请求—content-type与参数

    本文讲三种content-type以及在Jmeter中对应的参数输入方式 第一部分:目前工作中涉及到的content-type 有三种: content-type:在Request Headers里, ...

  5. chrome开发者工具那点事

    Elements:查找网页源代码HTML中的任一元素,手动修改任一元素的属性和样式且能实时在浏览器里面得到反馈. Console:记录开发者开发过程中的日志信息,且可以作为与JS进行交互的命令行She ...

  6. 10条Linux 命令了解服务器当前性能

    参考:http://www.infoq.com/cn/news/2015/12/linux-performance 1. uptime 如果电脑运行缓慢,执行 uptime 可以大致查看Linux服务 ...

  7. Objective-C 与命名空间

    http://blog.csdn.net/michelle__/article/details/52528172 Objective-C 在Objective-C应用中的所有类名都必须是全局唯一的.命 ...

  8. linux下如何实现mysql数据库每天自动备份定时备份

    版权声明:本文为 testcs_dn(微wx笑) 原创文章,非商用自由转载-保持署名-注明出处,谢谢.   目录(?)[+]   概述   备份是容灾的基础,是指为防止系统出现操作失误或系统故障导致数 ...

  9. 7、Android---网络技术

    玩手机不能上网是单机的时代 而且现在的流量也出了无限使用 几乎网络离不开人们的日常生活 7.1.WebView的用法 遇到一些特殊的请求 在程序中展示一些网页 加载和显示网页都是浏览器的任务 在不打开 ...

  10. 课后实践之mybash20155314

    课后实践之mybash 实践要求 加分题-mybash的实现 使用fork,exec,wait实现mybash 写出伪代码,产品代码和测试代码 发表知识理解,实现过程和问题解决的博客(包含代码托管链接 ...