【Python学习笔记之三】lambda表达式用法小结

除了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。

原文地址:https://www.cnblogs.com/ECJTUACM-873284962/p/8530264.html

时间: 2024-10-08 21:53:51

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

Python学习笔记——list的经常用法

python里list的经常用法都试用了一下,把功能和用法小结一下,这里不包含那些"__xxx__"形式的内置函数 #__author__ = 'hualong_zhang' # -*- coding:utf-8 -*- import sys reload(sys) sys.setdefaultencoding('utf-8') init_list_1 = [1, 4, 9, 'cat', 'dog', 'dog', 'bird', ['fish']] init_list_2 = [1

WPF-MVVM模式学习笔记4——Lambda表达式学习

在学习MVVM的过程中,其中自定义了一个超类NotificationObject,如下 public abstract class NotificationObject : INotifyPropertyChanged { public event PropertyChangedEventHandler PropertyChanged; protected virtual void RaisePropertyChanged(string propertyName) { PropertyChang

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

没有用Lambda表达式的写法:  Comparator<Transaction> byYear = new Comparator<Transaction>() {             @Override            public int compare(Transaction o1, Transaction o2) {                return o1.getValue().compareTo(o2.getValue());             

python学习笔记4-正则表达式

import re -- 所有正则表达式相关的功能都包含在re模块中 re.sub() -- 字符串替换 >>> import re >>> s= "100 NORTH BROAD ROAD" >>> re.sub('ROAD$','RD.',s) '100 NORTH BROAD RD.' >>> s = "100 BROAD" >>> re.sub('\\bROUAD$',

C++11学习笔记之三lamda表达式,std::function, std::bind

//lamda //first lamda [] {}; // second lamda []() //or no need () when paramater is null { std::cout << "second" << std::endl; }();// last add(), express will call this lamda func // 3 with return type auto kkk = []() { return 1; }()

python学习笔记3----正则表达式

正则表达式(RE)是通过re模块来实现的. 字符匹配: --普通字符: *大多数字母和字符一般都会和自身匹配. --元字符:. ^ $ * + ? {}  [] \ | () []: 通常用来指定一个指标集.其中的任何一个都可以进行匹配.[0-9],[a-zA-Z0-9],[a-z] 元字符在字符集中不起作用. 补集匹配不在区间范围内的字符. ^: 通常用来指定行首. $: 通常用来指定行尾. \: 反斜杠取消后面的元字符的特殊意思. 反斜杠后面加上不同的字符表示不同特殊意义. {m,n}: 把

【Java学习笔记之三十一】详解Java8 lambda表达式

Java 8 发布日期是2014年3月18日,这次开创性的发布在Java社区引发了不少讨论,并让大家感到激动.特性之一便是随同发布的lambda表达式,它将允许我们将行为传到函数里.在Java 8之前,如果想将行为传入函数,仅有的选择就是匿名类,需要6行代码.而定义行为最重要的那行代码,却混在中间不够突出.Lambda表达式取代了匿名类,取消了模板,允许用函数式风格编写代码.这样有时可读性更好,表达更清晰.在Java生态系统中,函数式表达与对面向对象的全面支持是个激动人心的进步.将进一步促进并行

Python学习笔记010——匿名函数lambda

1 语法 my_lambda = lambda arg1, arg2 : arg1 + arg2 + 1 arg1.arg2:参数 arg1 + arg2 + 1 :表达式 2 描述 匿名函数不需要return来返回值,表达式本身结果就是返回值. lambda 仅简洁了代码,并不会提高程序运行效率 如果可以用 for...in...if 来完成的,最好不使用匿名函数lambda 使用lambda时,函数内不要包含循环.嵌套:如果存在,则最好使用def函数来完成,这样的代码可读性和复用性均较高 l

【Python学习笔记之二】浅谈Python的yield用法

在上篇[Python学习笔记之一]Python关键字及其总结中我提到了yield,本篇文章我将会重点说明yield的用法 在介绍yield前有必要先说明下Python中的迭代器(iterator)和生成器(constructor). 一.迭代器(iterator) 在Python中,for循环可以用于Python中的任何类型,包括列表.元祖等等,实际上,for循环可用于任何“可迭代对象”,这其实就是迭代器 迭代器是一个实现了迭代器协议的对象,Python中的迭代器协议就是有next方法的对象会前