- lambda 是仅限于单个表达式的匿名函数。
- 它的最佳用途是作为简短的参数,尤其是
sorted、min和max的key。 - 列表推导式通常比带有 lambda 的
map或filter更清晰。 - 如果将 lambda 分配给名称,请改为写
def。
您需要按价格对产品进行排序。编写一个单独的命名函数只是为了说“使用价格”,感觉就像是很多仪式。 Python lambda 函数 让您可以在您使用它的地方定义内联的一小段逻辑。
Lambda 很方便,但也很容易过度使用。本指南展示了它们在哪些方面真正有帮助,以及在哪些方面使代码更难阅读。我们在 Python 3.13 上运行了每个示例,显示的输出来自该运行。
什么是 lambda?
lambda 是一个没有名称的函数,用 lambda 关键字编写:
lambda arguments: expression
它接受任意数量的参数并自动返回单个表达式的值。没有 return 关键字,也没有语句主体。
square = lambda x: x * x
print(square(4)) # 16
print((lambda a, b=10: a + b)(5)) # 15
第二行定义一个带有默认参数的 lambda 并立即调用它。您很少会在实际代码中编写此代码,但它表明 lambda 是一个普通的函数对象。
lambda 与 def
这两个函数产生的行为相同:
square = lambda x: x * x
def square_def(x):
return x * x
print(square(4), square_def(4)) # 16 16
print(square.__name__, square_def.__name__) # <lambda> square_def
看看名字。每个 lambda 都被称为 <lambda>,因此错误消息和日志无法告诉您哪一个失败了。这是每当您想要为函数命名时,PEP 8 等风格指南推荐 def 的主要原因。
| 特点 | 拉姆达 |
定义 |
|---|---|---|
| 名称 | 匿名 | 命名 |
| 身体 | 一个表情 | 任意数量的语句 |
| 返回 | 隐含的 | 显式 return |
| 文档字符串和类型提示 | 不支持 | 支持 |
| 可读的回溯 | 演出 <lambda> |
显示名称 |
| 最适合 | 简短的一次性争论 | 其他一切 |
用键排序:最佳用途
sorted、min 和 max 等函数接受 key 函数来决定比较什么。这就是 lambda 的闪光点:
products = [("pen", 10), ("bag", 900), ("book", 250)]
sorted(products, key=lambda p: p[1])
# [('pen', 10), ('book', 250), ('bag', 900)]
max(products, key=lambda p: p[1])
# ('bag', 900)
要按多项排序,请返回一个元组。在这里,人们按年龄排序,最年长的排在前面,然后按姓名排序:
people = [{"name": "Asha", "age": 31}, {"name": "Ben", "age": 25},
{"name": "Dev", "age": 31}]
[p["name"] for p in sorted(people, key=lambda p: (-p["age"], p["name"]))]
# ['Asha', 'Dev', 'Ben']
减号仅反转数字的顺序。有时您根本不需要 lambda,因为现有函数已经完成了这项工作。将默认排序与不区分大小写的排序进行比较:
names = ["Asha", "ben", "Chitra", "dev"]
sorted(names) # ['Asha', 'Chitra', 'ben', 'dev']
sorted(names, key=str.lower) # ['Asha', 'ben', 'Chitra', 'dev']
默认情况下将所有大写单词放在前面。通过 str.lower 可以修复这个问题,无需任何 lambda。 operator 模块提供类似的帮助器,例如 itemgetter(1) 和 lambda p: p[1]。
地图和过滤器
map 将函数应用于每个项目,filter 保留返回 true 的项目。两者都使用 lambda,但将它们与列表理解进行比较:
nums = [1, 2, 3, 4, 5, 6]
list(map(lambda n: n * 10, nums)) # [10, 20, 30, 40, 50, 60]
[n * 10 for n in nums] # [10, 20, 30, 40, 50, 60]
list(filter(lambda n: n % 2 == 0, nums)) # [2, 4, 6]
[n for n in nums if n % 2 == 0] # [2, 4, 6]
结果是相同的,并且大多数 Python 开发人员发现推导式更易于阅读。还要注意,map 和 filter 返回惰性迭代器,因此您需要 list() 才能查看这些值。我们的 Python 列表理解 指南深入介绍了这种风格。 map 仍然可以很好地读取现有的命名函数,如 map(str.strip, lines) 中所示。
减少
functools.reduce 将项目组合成单个值,携带运行结果:
from functools import reduce
reduce(lambda acc, n: acc + n, nums, 0) # 21
sum(nums) # 21
对于常见作业(例如总计),请使用 sum、max 或 any 等内置函数。对于极少数情况,请保留 reduce,如果 lambda 开始看起来很复杂,请考虑使用普通循环。
lambda 中的条件
您不能在 lambda 中使用 if 语句,但可以使用条件表达式:
label = lambda total: "large" if total >= 1000 else "small"
label(1200), label(300)
# ('large', 'small')
有一个条件就好了。一旦开始嵌套它们,读者就必须努力工作,这是切换到带有普通 if 语句的 def 的明显标志。
返回函数的 Lambda 表达式
函数可以创建并返回一个 lambda,该 lambda 会记住周围环境中的值:
def multiplier(n):
return lambda x: x * n
double = multiplier(2)
double(7) # 14
这是因为闭包的作用,我们关于 JavaScript 闭包 的文章用不同的语言解释了闭包。 Python 的 functools.partial 提供了另一种预填充参数的方法。
后期绑定陷阱
这最终几乎吸引了所有人。我们在循环中创建了三个 lambda,期望每个返回自己的数字:
funcs = [lambda: i for i in range(3)]
[f() for f in funcs]
# [2, 2, 2]
所有三个都返回 2。 lambda 在创建时不会捕获 i 的 value。它在 调用 时查找变量,然后循环已完成,将 i 保留为 2。通常的修复方法是将值捕获为默认参数,因为默认值是在定义时计算的:
funcs = [lambda i=i: i for i in range(3)]
[f() for f in funcs]
# [0, 1, 2]
这并不是 lambda 所独有的,因为循环内的 def 的行为方式相同。它只是更频繁地出现在 lambda 中,因为它们通常在循环中创建,例如在附加按钮回调时。
提示: 如果您需要每个项目一个功能,请考虑是否需要很多功能。通常,将项目作为参数的单个函数会更清晰,并且完全避免了陷阱。
小函数查找表
Lambda 非常适合将名称映射到小型操作的字典:
ops = {
"add": lambda a, b: a + b,
"sub": lambda a, b: a - b,
}
ops["sub"](10, 3) # 7
这可以取代if和elif的长链。 Python 字典 指南涵盖了更多字典模式,operator 模块已经提供了 operator.add 和 operator.sub。
lambda 的限制
- 仅一个表达式。 没有
=赋值,没有循环,也没有try块。 - 没有语句。 正如我们的测试所证实的那样,写入
lambda x: return x失败并返回SyntaxError。 - 没有文档字符串或类型提示。 工具和读者获得的帮助较少。
- 更困难的调试。 错误指向
<lambda>而不是有用的名称。
当然,Lambda 仍然可以引发异常。处理这些问题的方式与处理任何函数的方式相同,如 Python 异常处理 中所述。
何时不使用 lambda
| 情况 | 更好的选择 |
|---|---|
| 为其分配一个名称 | 定义 |
| 逻辑需要不止一行 | 定义 |
| 转换或过滤列表 | 列表理解 |
| 求和或求最大值 | sum、max、min |
| 获取项目或属性 | operator.itemgetter 或 attrgetter |
| 相同的 lambda 出现在多个地方 | 您可以测试的命名函数 |
最后一点很重要。值得重用的逻辑值得测试,并且命名函数更容易测试,正如我们的 pytest 教程 所示。
最后的话
好的 lambda 足够短,一目了然,并且使用一次,通常作为参数。排序键就是典型的例子。要检查您自己的代码,请搜索 = lambda,并将其转换为命名函数。然后用 lambda 查看任何 map 或 filter,看看推导式是否读起来更好。
常见问题
Python 中的 lambda 函数是什么?
它是一个使用 lambda 关键字创建的小型匿名函数。它接受参数并返回单个表达式的值,没有名称或返回语句。
Python lambda 可以有多行吗?
不可以。lambda 仅限于一个表达式。您可以将其分割成括号内的行,但它仍然不能包含语句,因此使用 def 来实现更复杂的逻辑。
我可以在 lambda 中使用 if else 吗?
您可以使用条件表达式,例如 lambda x: “big” if x > 10 else “small”。不允许使用带有块的 if 语句。
lambda 比普通函数更快吗?
没有意义。两者都创建相同类型的函数对象。在它们之间进行选择是为了可读性,而不是性能。
我应该将 map 与 lambda 或列表理解一起使用吗?
为了提高可读性,大多数 Python 开发人员更喜欢列表理解。当您已经有一个命名函数时,map 是合理的,例如map(str.strip,lines)。
继续学习
需要测试、WordPress 或 SEO 方面的帮助?
我们帮助团队交付更好的软件并发展他们的网站。告诉我们您正在做什么,我们将建议明确的下一步。
更多指南 测试、人工智能和现代开发
喜欢这篇文章吗?选择一个主题并直接跳至我们最新的教程。
人工智能幻觉解释:语言模型为何会产生幻觉以及如何减少它
人工智能幻觉解释:它们是什么,为什么语言模型产生它们,常见类型,如何通过基础和提示来减少它们,以及我们进行的简单检查。
阅读文章 →DevOps环境变量解释:.env 文件、秘密和配置正确完成
使用我们运行的 Python 示例解释环境变量:读取和设置它们、为什么每个值都是字符串、.env 文件、进程继承、秘密和验证。
阅读文章 →软件开发API 速率限制解释:算法、标头和处理 429 错误
API 速率限制通过我们运行的模拟进行了解释:令牌桶、固定窗口、滑动窗口、429 响应、速率限制标头、带回退的重试和测试限制。
阅读文章 →测试自动化Python 中的模拟:unittest.mock、补丁以及导致测试谎言的错误
使用unittest.mock 在Python 中进行模拟,经过测试:模拟和return_value、side_effect、修补使用名称的位置、spec 和autospec、模拟日期以及常见错误。
阅读文章 →软件测试代码覆盖率解释:行覆盖率与分支覆盖率以及 100% 的真正含义
用真实的coverage.py运行来解释代码覆盖率:行、分支和条件覆盖率,阅读报告,为什么100%覆盖率仍然会错过错误,以及合理的目标。
阅读文章 →数据库与 SQLSQL 中的 DELETE、TRUNCATE 和 DROP:差异、回滚以及何时使用它们
DELETE、TRUNCATE 和 DROP 通过经过测试的 SQL 进行了解释:各自删除什么、回滚、触发器、身份重置、速度,以及 PostgreSQL、MySQL、SQL Server 和 SQLite 有何不同。
阅读文章 →数据库与 SQLSQL 视图解释:创建、使用和避免陷阱(附示例)
SQL 视图通过我们运行的查询进行解释:创建视图、实时结果、隐藏列、性能、可更新和物化视图、破坏的依赖关系和最佳实践。
阅读文章 →软件开发TypeScript 接口与类型:真正的差异,使用编译器进行测试
TypeScript 接口与类型,使用真实编译器进行检查:两者都可以做什么,只有一个可以做什么,声明合并,扩展与交集以及要遵循的简单规则。
阅读文章 →软件开发Python *args 和 **kwargs 解释:灵活的函数和清晰的示例
Python *args 和 **kwargs 用我们运行的代码进行了解释:打包和解包、参数顺序、仅关键字和仅位置参数、装饰器和常见错误。
阅读文章 →软件开发Python Lambda 函数:语法、实际用途以及何时不使用它们
Python lambda 函数用我们运行的代码进行了解释:语法、使用键排序、映射和过滤器、闭包、后期绑定陷阱,以及何时普通 def 是更好的选择。
阅读文章 →人工智能什么是人工智能代理?它们是如何工作的,只需运行一个简单的代理循环
什么是人工智能代理?关于代理循环、工具、内存和护栏的简单指南,以及我们运行的一个小型 Python 循环、真正的风险以及何时简单的聊天机器人是更好的选择。
阅读文章 →DevOps监控与可观察性:真正的区别,包括日志、指标和跟踪
监控与可观察性解释:已知和未知的故障、日志、指标和跟踪、检查事件演练、黄金信号、OpenTelemetry 和有用的警报。
阅读文章 →DevOpsDocker Volumes 与 Bind Mounts:以正确的方式保留容器数据
Docker 卷与绑定挂载解释:为什么容器数据消失、命名卷、绑定挂载、tmpfs、Compose 示例、备份、权限以及何时使用它们。
阅读文章 →测试自动化TDD 与 BDD:主要区别、以两种方式构建的一个功能以及何时使用每种方式
TDD 与 BDD 的解释是通过一种双向构建的折扣功能:使用我们运行的代码进行红绿重构、Given-When-Then 场景、工具、权衡以及何时使用每种方案。
阅读文章 →软件开发Git merge 与 rebase 的区别:示例及如何选择
用真实的 log 输出、对比表、rebase 黄金法则和安全的团队工作流,讲清 Git merge 与 rebase 的区别。
阅读文章 →软件开发Python 列表推导式:语法、示例与何时避免使用
Python 列表推导式详解:语法、过滤、if-else、嵌套循环、实测耗时,以及何时普通 for 循环更合适。
阅读文章 →数据库与 SQLSQL JOIN 详解:附示例(INNER、LEFT、RIGHT、FULL)
通过示例表和查询,一步步讲解 SQL JOIN:INNER、LEFT、RIGHT、FULL、CROSS 和 SELF,并介绍常见错误与性能技巧。
阅读文章 →