除了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. Mac环境下WingIDE切换python版本

    https://www.cnblogs.com/fastLearn/p/6514442.html

  2. 张高兴的 Windows 10 IoT 开发笔记:无线收发芯片 nRF24L01

    This is a Windows 10 IoT Core project on the Raspberry Pi 2/3, coded by C#. GitHub:https://github.co ...

  3. virtualbox 错误解决记录

    1,E_INVALIDARG (0x80070057),virtualbox中Cannot register the hard disk错误解决办法 virtualbox中加载已有的虚拟硬盘时出现Ca ...

  4. 在远程登陆的主机上通过命令行源码编译安装 GNU M4、autoconf、automake 等程序

    由于实验需要,最近获得了一个实验室服务器的账号,平常主要通过 ssh 进行远程登陆进行实验.一方面,远程登录的机器只提供终端界面,一般只通过命令行进行任务操作:另一方面,由于是多人共享服务器,故而个人 ...

  5. kvo的observationInfo

    观察者信息的注册: <NSKeyValueObservationInfo 0x600000708d60> ( <NSKeyValueObservance 0x6000009143f0 ...

  6. UVa 11440 - Help Tomisu(欧拉函数 + 问题转换)

    链接: https://uva.onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8&page=show_problem& ...

  7. 1070. [SCOI2007]修车【费用流】

    Description 同一时刻有N位车主带着他们的爱车来到了汽车维修中心.维修中心共有M位技术人员,不同的技术人员对不同 的车进行维修所用的时间是不同的.现在需要安排这M位技术人员所维修的车及顺序, ...

  8. [AHOI2009]飞行棋

    嘟嘟嘟 刚开始想这道题的时候确实很蒙,只想到矩形对边做对应的弧长相等,然后想办法凑出相等的弧长.其实正解很简单,不要去想边,应该想对角线,因为根据初中园的知识,这个矩形的对角线是圆的直径,而直径所对的 ...

  9. 范数 L1 L2

    在线性代数,函数分析等数学分支中,范数(Norm)是一个函数,是赋予某个向量空间(或矩阵)中的每个向量以长度或大小的函数.对于零向量,令其长度为零.直观的说,向量或矩阵的范数越大,则我们可以说这个向量 ...

  10. Jython的应用

    今天本文围绕主要内容是jython是什么.安装.简单实用. 另外说说我为什么研究jython,研究它是有一个目的的,目的是将python代码转化为jar包以供安卓方面那边人脸识别,虽说目前人脸识别像阿 ...