深入理解python的Lambda

举报
子都爱学习 发表于 2021/10/14 19:45:06 2021/10/14
【摘要】 如果您刚开始使用 Python,那么理解 lambda 是什么可能有点令人困惑。也许你看完这篇就够了,从底层到熟练使用。lambda 也称为匿名函数,这是因为 lambda 没有名称。要在 Python 中定义 lambda,您可以使用关键字 lambda 后跟一个或多个参数、一个冒号 (:) 和一个表达式。我们将从一个简单的 lambda 函数示例开始来熟悉它的语法,然后我们将看看 Pyt...

如果您刚开始使用 Python,那么理解 lambda 是什么可能有点令人困惑。也许你看完这篇就够了,从底层到熟练使用。

lambda 也称为匿名函数,这是因为 lambda 没有名称。要在 Python 中定义 lambda,您可以使用关键字 lambda 后跟一个或多个参数、一个冒号 (:) 和一个表达式。

我们将从一个简单的 lambda 函数示例开始来熟悉它的语法,然后我们将看看 Python lambda 函数如何适应不同的场景。

为了练习所有示例,我们将使用 Python 交互式 shell。

如何在 Python 中使用 Lambda

让我们从 lambda 函数的语法开始。

lambda 函数以lambda关键字开头,后跟逗号分隔的参数列表。下一个元素是一个冒号(:) 后跟一个表达式

lambda <argument(s)> : <expression>

如您所见,可以在一行中定义一个 lambda 函数。

让我们看一个非常简单的 lambda,它将数字 x(参数)乘以 2:

lambda x : 2*x

如果我在 Python shell 中定义这个 lambda,会发生以下情况:

>>> lambda x : 2*x
<function <lambda> at 0x101451cb0>

我得到一个函数对象。有趣的是,当我定义 lambda 时,我不需要 return 语句作为表达式的一部分。

如果我在表达式中包含 return 语句会发生什么?

>>> lambda x : return 2*x
  File "<stdin>", line 1
    lambda x : return 2*x
                    ^
SyntaxError: invalid syntax

我们收到一个语法错误。因此,无需在 lambda 中包含 return。

如何在 Python 中调用 Lambda 函数

我们已经看到了如何定义一个 lambda,但是我们如何调用它呢?

首先,我们将在不将函数对象分配给变量的情况下执行此操作。为此,我们只需要使用括号。

(lambda x : 2*x)(2)

我们将用括号将 lambda 表达式括起来,然后用括号将要传递给 lambda 的参数括起来。

这是我们运行时的输出:

>>> (lambda x : 2*x)(2)
4

我们还有另一种选择。我们可以将 lambda 函数返回的函数对象赋值给一个变量,然后使用变量名调用该函数。

>>> multiply = lambda x : 2*x
>>> multiply(2)
4

我觉得这与不给 lambda 命名的想法背道而驰,但值得知道……

在继续阅读本文之前,请确保您尝试了迄今为止我们看到的所有示例以熟悉 lambda。

我仍然记得我第一次开始阅读 lambdas 时,我有点困惑。所以如果你现在也有同样的感觉,请不要担心🙂

将多个参数传递给 Lambda 函数

在前面的部分中,我们已经看到了如何定义和执行 lambda 函数。

我们还看到 lambda 可以有一个或多个参数,让我们看一个有两个参数的例子。

创建一个将参数 x 和 y 相乘的 lambda:

lambda x, y :  x*y

如您所见,这两个参数用逗号分隔。

>>> (lambda x, y :  x*y)(2,3)
6

正如预期的那样,输出返回正确的数字 (2*3)。

lambda 是一个IIFE(立即调用函数表达式)。这基本上是一种说法,即 lambda 函数一经定义就立即执行。

Lambda 函数和常规函数之间的区别

在继续研究如何在 Python 程序中使用 lambda 之前,重要的是要了解常规 Python 函数和 lambda 如何相互关联。

让我们以前面的例子为例:

lambda x, y :  x*y

我们也可以使用def关键字将其编写为常规函数:

def multiply(x, y):
    return x*y

与 lambda 形式相比,您会立即注意到三个不同之处:

  1. 使用 def 关键字时,我们必须为我们的函数指定一个名称。
  2. 这两个参数用括号括起。
  3. 我们使用return语句返回函数的结果。

将 lambda 函数分配给变量是可选的(如前所述):

multiply_lambda = lambda x, y :  x*y

让我们比较这两个函数的对象:

>>> def multiply(x, y):
...     return x*y
... 
>>> multiply_lambda = lambda x, y :  x*y
>>> multiply
<function multiply at 0x101451d40>
>>> multiply_lambda
<function <lambda> at 0x1014227a0>

在这里我们可以看到一个区别:使用 def 关键字定义的函数由名称“multiply”标识,而 lambda 函数由通用 <lambda> 标签标识。

让我们看看type() 函数在应用于这两个函数时返回什么:

>>> type(multiply)
<class 'function'>
>>> type(multiply_lambda)
<class 'function'>

所以,这两个函数的类型是一样的。

我可以在 Python Lambda 中使用 If Else 吗?

我想知道我是否可以在 lambda 函数中使用 if else 语句......

lambda x: x if x > 2 else 2*x

如果 x 大于 2,这个 lambda 应该返回 x,否则它应该返回 x 乘以 2。

首先,让我们确认它的语法是否正确…

>>> lambda x: x if x > 2 else 2*x
<function <lambda> at 0x101451dd0>

到目前为止没有错误......让我们测试我们的功能:

>>> (lambda x: x if x > 2 else 2*x)(1)
2
>>> (lambda x: x if x > 2 else 2*x)(2)
4
>>> (lambda x: x if x > 2 else 2*x)(3)
3

它运行良好,同时你可以看到,如果我们让 lambda 表达式越来越复杂,我们的代码会变得更难阅读。

正如本教程开头提到的:一个 lambda 函数只能有一个表达式。与常规函数相比,这使其适用于数量有限的用例

一个 lambda 表达式中不能有多个语句

如何用 Lambda 和 Map 替换 For 循环

在本节中,我们将看到 lambda 表达式在应用于Python 列表等可迭代对象时如何变得非常强大。

让我们从一个标准的Python for 循环开始,它遍历字符串列表的所有元素并创建一个新列表,其中所有元素都是大写的。

countries = ['Italy', 'United Kingdom', 'Germany']
countries_uc = []

for country in countries:
    countries_uc.append(country.upper())

这是输出:

>>> countries = ['Italy', 'United Kingdom', 'Germany']
>>> countries_uc = []
>>> 
>>> for country in countries:
...     countries_uc.append(country.upper())
... 
>>> print(countries_uc)
['ITALY', 'UNITED KINGDOM', 'GERMANY']

现在我们将编写相同的代码,但使用 lambda。为此,我们还将使用名为map的 Python 内置函数,该函数具有以下语法:

map(function, iterable, ...)

地图功能需要另一个功能第一个参数,然后iterables的列表。在这个特定示例中,我们只有一个可迭代对象,即国家/地区列表。

你以前见过一个以另一个函数作为参数的函数吗?

将另一个函数作为参数的函数称为高阶函数。

听起来可能很复杂,这个例子将帮助你理解它是如何工作的。那么,map函数有什么作用呢?

map 函数返回一个可迭代对象,它是作为应用于可迭代对象的每个元素的第一个参数传递的函数的结果。

在我们的场景中的功能,我们将通过作为第一个参数是一个lambda函数,其参数转换为大写格式。作为迭代,我们将传递我们的列表

map(lambda x: x.upper(), countries)

我们要不要尝试执行它?

>>> map(lambda x: x.upper(), countries)
<map object at 0x101477890>

我们得到一个地图对象。我们怎样才能取回一个列表呢?

我们可以将地图对象转换为列表……

>>> list(map(lambda x: x.upper(), countries))
['ITALY', 'UNITED KINGDOM', 'GERMANY']

很明显,与我们使用 for 循环的代码相比,使用 map 和 lambda 使这段代码更加简洁。

将 Lambda 函数与字典结合使用

我想尝试使用 lambda 函数从字典列表中提取特定字段。

这是可以应用于许多场景的东西。

这是我的字典列表:

people = [{'firstname':'John', 'lastname':'Ross'}, {'firstname':'Mark', 'lastname':'Green'}]

再一次,我可以将 map 内置函数与 lambda 函数一起使用。

lambda 函数将一个字典作为参数并返回 firstname 键的值。

lambda x : x['firstname']

完整的地图表达式为:

firstnames = list(map(lambda x : x['firstname'], people))

让我们运行它:

>>> firstnames = list(map(lambda x : x['firstname'], people))
>>> print(firstnames)
['John', 'Mark']

将 Lambda 传递给过滤器内置函数

另一个可以与 lambdas 一起使用的 Python 内置函数是filter 函数

下面你可以看到它的语法需要一个函数和一个可迭代的:

filter(function, iterable)

这里的想法是创建一个表达式,给定一个列表,返回一个新列表,该列表的元素与 lambda 函数定义的特定条件匹配。

例如,给定一个数字列表,我想返回一个只包含负数的列表。

这是我们将使用的 lambda 函数:

lambda x : x < 0

让我们尝试执行这个 lambda,向它传递几个数字,以便清楚 lambda 返回什么。

>>> (lambda x : x < 0)(-1)
True
>>> (lambda x : x < 0)(3)
False

我们的 lambda 返回一个布尔值:

  • 如果参数是否定的,则为真。
  • 如果参数为正,则为 False。

现在,让我们将此 lambda 应用于过滤器函数

>>> numbers = [1, 3, -1, -4, -5, -35, 67]
>>> negative_numbers = list(filter(lambda x : x < 0, numbers))
>>> print(negative_numbers)
[-1, -4, -5, -35]

我们得到了预期的结果,一个包含所有负数的列表。

与 map 函数相比,你能看出区别吗?

filter 函数返回一个列表,其中包含初始列表中元素的子集。

如何将 Reduce 和 Lambda 与列表一起使用

另一个常见的 Python 内置函数是属于functools 模块reduce 函数

reduce(function, iterable[, initializer])

在此示例中,我们将忽略初始化程序,您可以在此处找到有关它的更多详细信息。

reduce 函数有什么作用?

给定一个值列表:

 [v1, v2, ..., vn]

它将作为参数传递的函数应用于可迭代对象的前两个元素。结果是:

[func(v1,v2), v3, ..., vn]

然后它将函数应用于前一次迭代的结果和列表中的下一个元素:

[func(func(v1,v2),v3), v4, ..., vn]

这个过程从左到右继续,直到到达列表中的最后一个元素。最终结果是一个数字

为了在实践中理解它,我们将应用一个简单的 lambda 来计算两个数字的总和到一个数字列表:

>>> reduce(lambda x,y: x+y, [3, 7, 10, 12, 5])
37

下面是计算结果的方法:

((((3+7)+10)+12)+5)

让我们看看我们是否也可以使用 reduce 函数来连接列表中的字符串:

>>> reduce(lambda x,y: x + ' ' + y, ['This', 'is', 'a', 'tutorial', 'about', 'Python', 'lambdas'])
'This is a tutorial about Python lambdas'

应用于类的 Lambda 函数

考虑到 lambdas 可以用来代替常规的 Python 函数,我们可以使用 lambdas 作为类方法吗?

让我们一探究竟吧!

我将定义一个名为 Gorilla的类,它包含一个构造函数和打印消息的 run 方法:

class Gorilla:
    def __init__(self, name, age, weight):
        self.name = name
        self.age = age
        self.weight = weight

    def run(self):
        print('{} starts running!'.format(self.name))

然后我创建一个名为 Spartacus 的类的实例并在其上执行 run 方法:

Spartacus = Gorilla('Spartacus', 35, 150)
Spartacus.run()

输出是:

Spartacus starts running!

现在,让我们用 lambda 函数替换 run 方法:

run = lambda self: print('{} starts running!'.format(self.name))

以我们在上面一节中所做的相同方式,我们将 lambda 返回的函数对象分配给变量 run。

另请注意:

  • 我们删除了 def 关键字,因为我们用 lambda 替换了常规函数。
  • lambda 的参数是self的实例。

在 Gorilla 类的实例上再次执行 run 方法.您将看到输出消息完全相同。

这表明我们可以使用 lambdas 作为类方法!

您可以根据使您的代码易于维护和理解的因素来选择您喜欢哪一种。

将 Lambda 与 Sorted 函数结合使用

sorted 内置函数从可迭代对象返回一个排序列表。

让我们看一个简单的例子,我们将对一个包含一些行星名称的列表进行排序:

>>> planets = ['saturn', 'earth', 'mars', 'jupiter']
>>> sorted(planets)
['earth', 'jupiter', 'mars', 'saturn']

如您所见, sorted 函数按字母顺序对列表进行排序。

现在,假设我们想根据不同的标准对列表进行排序,例如每个单词的长度。

为此,我们可以使用附加参数,该允许在进行任何比较之前提供应用于每个元素的函数。

>>> sorted(planets, key=len)
['mars', 'earth', 'saturn', 'jupiter']

在这种情况下,我们使用了 len() 内置函数,这就是行星从最短到最长排序的原因。

那么,lambdas 在哪里适合所有这些?

Lambda 是函数,因此它们可以与 key 参数一起使用。

例如,假设我想根据每个行星的第三个字母对我的列表进行排序。

这是我们如何做到的:

>>> sorted(planets, key=lambda p: p[2])
['jupiter', 'earth', 'mars', 'saturn']

如果我想根据特定属性的值对字典列表进行排序怎么办?

>>> people = [{'firstname':'John', 'lastname':'Ross'}, {'firstname':'Mark', 'lastname':'Green'}]
>>> sorted(people, key=lambda x: x['lastname'])
[{'firstname': 'Mark', 'lastname': 'Green'}, {'firstname': 'John', 'lastname': 'Ross'}]

在这个例子中,我们根据姓氏键的值对字典列表进行了排序。

Python Lambda 和错误处理

在我们查看 lambdas 和常规函数之间的区别的部分中,我们看到了以下内容:

>>> multiply
<function multiply at 0x101451d40>
>>> multiply_lambda
<function <lambda> at 0x1014227a0>

其中multiply 是一个常规函数,而multiply_lambda 是一个lambda 函数。

如您所见,常规函数的函数对象由名称标识,而 lambda 函数对象由通用 <lambda> 名称标识。

这也使得 lambda 函数的错误处理变得更加棘手,因为 Python 回溯不包括发生错误的函数的名称。

让我们创建一个常规函数并将其传递给它会导致 Python 解释器引发异常的参数:

def calculate_sum(x, y):
    return x+y

print(calculate_sum(5, 'Not_a_number'))

当我在 Python shell 中运行此代码时,出现以下错误:

>>> def calculate_sum(x, y):
...     return x+y
... 
>>> print(calculate_sum(5, 'Not_a_number'))
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 2, in calculate_sum
TypeError: unsupported operand type(s) for +: 'int' and 'str'

从回溯中我们可以清楚地看到错误发生在calculate_sum 函数的第2 行。

现在,让我们用 lambda 替换这个函数:

calculate_sum = lambda x, y: x+y
print(calculate_sum(5, 'Not_a_number'))

输出是:

>>> calculate_sum = lambda x,y: x+y
>>> print(calculate_sum(5, 'Not_a_number'))
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 1, in <lambda>
TypeError: unsupported operand type(s) for +: 'int' and 'str'

异常的类型和错误信息是一样的,但是这次回溯告诉我们在函数 <lambda> 的第 1 行出现了错误。

想象一下,如果您必须在 10,000 行代码中找到正确的行。

这是在可能的情况下使用常规函数而不是 lambda 函数的另一个原因。

将变量的参数列表传递给 Python Lambda

在本节中,我们将看到如何为 Python lambda 提供可变参数列表。

要将可变数量的参数传递给 lambda,我们可以像使用常规函数一样使用*args


(lambda *args: max(args))(5, 3, 4, 10, 24)

当我们运行它时,我们得到传递给 lambda 的参数之间的最大值:

>>> (lambda *args: max(args))(5, 3, 4, 10, 24)
24

我们不一定必须使用关键字 args。重要的是在 Python 中 args 之前的 * 表示可变数量的参数。

让我们通过用数字替换 args 来确认是否是这种情况:

>>> (lambda *numbers: max(numbers))(5, 3, 4, 10, 24)
24

Lambda 函数的更多示例

在完成本教程之前,让我们看一下更多的 lambda 示例。

如果您想在 Python 程序中使用 lambda,这些示例应该会给您更多想法。

给定 Linux 命令列表,仅返回以字母“c”开头的命令:

>>> commands = ['ls', 'cat', 'find', 'echo', 'top', 'curl']
>>> list(filter(lambda cmd: cmd.startswith('c'), commands))
['cat', 'curl']

从带有空格的逗号分隔字符串返回一个列表,其中包含字符串中没有空格的每个单词:

>>> weekdays = "monday   , tuesday, wednesday,thursday,   friday,  saturday  ,sunday"
>>> list(map(lambda word: word.strip(), weekdays.split(',')))
['monday', 'tuesday', 'wednesday', 'thursday', 'friday', 'saturday', 'sunday']

使用 Python range 函数生成数字列表并返回大于 4 的数字:

>>> list(filter(lambda x: x > 4, range(15)))
[5, 6, 7, 8, 9, 10, 11, 12, 13, 14]
【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。