0%

Python 中的Lambda 函数:他们有什么用 (译)

本文用于练习英文阅读,如有侵权,联系删除

原文链接Dan Bader的 Lambda Functions in Python: What Are They Good For?

以下为译文
Python lambda 表达式: 它们的优点,何时使用以及何时最好避免使用他们。

Python中的lambda关键词提供了声明简短匿名函数的快捷方式。Lambda 函数的行为就像使用def关键字声明的常规函数一样。只要函数对象被声明就可以使用。

例如,这是定义一个加法功能的简单lambda函数:

1
2
3
>>> add = lambda x, y: x + y
>>> add(5, 3)
8

你也可以使用def关键字声明同样的加法函数:

1
2
3
4
>>> def add(x, y):
... return x + y
>>> add(5, 3)
8

现在你可能感到惊奇:为什么对lambdas大惊小怪?如果它只是def声明函数的简洁版,有什么大不了的?

看下面这个例子,并把执行这个操作时把这个函数表达式放在脑海里:

1
2
>>> (lambda x, y: x + y)(5, 3)
8

好的,这是怎么回事?我只是用lambda内联定义了一个add 函数,接着用参数5和3进行了调用。

从概念上讲lambda表达式 lambda x,y:x+y 和用def声明的函数类似,只是用了内联写法。区别在于使用它之前没有绑定到诸如add这样的名称上。我只是说了我想计算的表达式,接着像常规函数一样调用它立刻求值。

在你继续之前,你可能需要玩味一下前面的代码示例,以便真的理解它的意思。我仍然记得这花了好久去思考。所以要担心花费几分钟去理解它。

lambdas 和常规函数的另一个语法不同在于:Lamdba 只能是单个表达式。这意味着lambda函数不能使用声明和注释,甚至return语句。

那么该如何使lambdas返回值?执行lambda函数会评估其表达式,然后自动返回结果。因此,总会有一个隐式的return语句。这就是为什么有些人将lambda称为单表达式函数的原因。

Lambda的使用

什么时候你可以使用lambda函数在你的代码中?从技术上讲,任何只要希望提供函数对象的时候都可以使用lambda表达式。并且因为lambda表达式是可以匿名的,你甚至不需要声明它。

这提供一个方便的非官方的快捷定义Python函数的方法。我最常用到的lambda示例是按照指定键指定规则进行排序:

1
2
>>> sorted(range(-5, 6), key=lambda x: x ** 2)
[0, -1, 1, -2, 2, -3, 3, -4, 4, -5, 5]

像常规函数一样,lambda也可以用作词法闭包。

什么是词法闭包?只是对一种特殊的函数功能的称呼。即使程序流不再位于该作用域,他也能记住封闭的词法作用域的值。这是一个(很学术)的例子来说明这个想法:

1
2
3
4
5
6
7
8
9
10
>>> def make_adder(n):
... return lambda x: x + n

>>> plus_3 = make_adder(3)
>>> plus_5 = make_adder(5)

>>> plus_3(4)
7
>>> plus_5(4)
9

在上面例子中 即使 lambda x+n 包含在 make_adder 函数的的作用域中,仍然可以访问n的值。

有时,使用lambda函数代替使用def声明的嵌套函数可以更清晰地表达自己的意图。但是,老实说这并不常见————至少我很少这么写代码。

不滥用Lambda

现在,一方面,我希望本文使你对Python lambda函数感兴趣。另一方面,我觉得需要提出一个警告:Lambda 函数应该被谨慎使用。

我知道,我使用了lambda表达式编写了一部分看起来“很酷”的代码,但是这对我和我的同事通常是一个负担。如果你想使用lambda,请花几秒(或几分钟)去思考这是否是干净可维护的代码。

例如,写这样的两行代码去保存变量值是愚蠢的。当然,从技术上讲它是有效的,并且是个很好的“技巧”。但是这会让下一个在有限时间内维护它的人感到困惑:

1
2
3
4
5
6
7
8
# Harmful:
>>> class Car:
... rev = lambda self: print('Wroom!')
... crash = lambda self: print('Boom!')

>>> my_car = Car()
>>> my_car.crash()
'Boom!'

使用复杂的map() 或 filter() 会让我产生和使用lambda相同的困惑。通常用list生成式或生成器会更干净:

1
2
3
4
5
6
7
# 糟糕的:
>>> list(filter(lambda x: x % 2 == 0, range(16)))
[0, 2, 4, 6, 8, 10, 12, 14]

# 更好的:
>>> [x for x in range(16) if x % 2 == 0]
[0, 2, 4, 6, 8, 10, 12, 14]

如果你发现自己使用一个复杂的lambda表达式,请考虑用合适的名称定义一个函数代替它。

从长远来看,节省一些键盘敲击无关紧要。你的同事(和将来的你)更喜欢干净易读的代码。

要记住的事情

  • Lambda 函数不必绑定命名(匿名)
  • Lambda 不能使用常规的Python语句,并且始终包含隐式的return语句
  • 总是问自己: 使用常规(命名)函数或生成器是否会更清晰。

欢迎关注我的其它发布渠道