Python 支持函数式编程的核心武器有两个:lambda(匿名函数)和高阶函数(把函数当参数或返回值的函数)。前面 函数定义与调用 里说过”函数是一等公民”,这一篇就把这条规则的用法讲透——lambda、map、filter、reduce、sorted 的 key、functools.partial,用完之后你的代码会明显变短、变优雅。
一、lambda 语法
lambda 定义一个匿名的、只能写一个表达式的函数:
1 | # 传统函数 |
结构:lambda 参数列表: 表达式。没有 return,表达式的值就是返回值。
限制:
- 只能一个表达式,不能有语句(
if x: return y不行); - 不能有
return; - 不能有多行;
- 匿名——没有名字(虽然可以赋给变量)。
多参数:
1 | add = lambda a, b: a + b |
二、什么时候用 lambda
lambda 的唯一价值是”传给别的函数当参数”——用完即扔。
1 | # 排序:按字符串长度 |
不建议给 lambda 起名:
1 | # ❌ 反面教材:赋名字给 lambda |
Guido 本人和 PEP 8 都认为——如果 lambda 需要名字,那你应该用 def。
三、高阶函数:函数作为参数
Python 内置了几个经典高阶函数:map、filter、reduce、sorted、min/max、any/all。
map(func, iter)
对每个元素调用 func:
1 | list(map(str.upper, ["a", "b", "c"])) # ['A', 'B', 'C'] |
filter(pred, iter)
保留返回真的元素:
1 | list(filter(lambda x: x > 0, [-2, -1, 0, 1, 2])) # [1, 2] |
reduce(func, iter, initial)
累积计算,需要从 functools 导入:
1 | from functools import reduce |
reduce 曾经是 Python 内置的,Python 3 里移到了 functools,Guido 认为大部分场景用推导式或者 sum/min/max 就够,不需要 reduce。
map/filter vs 推导式
现代 Python 更推荐列表推导式:
1 | # map + filter |
推导式一般更好读。map/filter 适合”已有函数不用 lambda”的场景:
1 | # ✅ 简洁 |
四、sorted / min / max 的 key
高阶函数最常见的用法:给排序函数传 key。
1 | words = ["Banana", "apple", "cherry"] |
用 operator 更快:
1 | from operator import itemgetter, attrgetter |
itemgetter/attrgetter 是 C 实现,比 lambda 快很多,代码也更清晰。
五、functools.partial:偏函数
partial 固定函数的一部分参数,产生新函数:
1 | from functools import partial |
用途:创建”预配置”的函数。特别适合传给 map/filter/sort 等高阶函数。
六、operator 模块:函数化的运算符
operator 模块把运算符变成函数,可以传参:
1 | from operator import add, mul, gt, itemgetter, methodcaller |
七、函数作为返回值:函数工厂
1 | def make_greeter(greeting): |
这本质就是闭包(作用域与闭包 讲过)。
八、装饰器预告
高阶函数最重要的应用就是装饰器——一个”接受函数、返回新函数”的函数:
1 | def uppercase(func): |
我们在 装饰器入门 会详细讲。
九、函数式编程的边界
Python 支持函数式编程,但它不是纯函数式语言。跟 Haskell 或 Lisp 相比:
- Python 更强调命令式和面向对象;
- 没有尾调用优化,不适合大量递归;
- lambda 有限制,不能像 Haskell 那样自由;
- 推导式在很多场景比 map/filter 更 Pythonic。
建议:把函数式当”工具”,不当”信仰”。合适的地方用,别为了”函数式”而扭曲代码。
十、一个综合例子:数据管道
1 | # pipeline.py |
十一、常见陷阱
- lambda 里想写多行/if-return:不行,改回 def。
- 给 lambda 命名:PEP 8 反对。
- 循环里创建 lambda 的晚绑定:见 作用域与闭包。
- map/filter 结果是迭代器:
filter(...)只能遍历一次,多次遍历要list()。 - reduce 空序列:
reduce(add, [])抛错,除非给 initial。 partial位置参数只能从左到右固定:想固定中间的参数用关键字:partial(f, y=10)。operator.itemgetter传多个索引:返回元组:itemgetter(0, 2)("abc") == ('a', 'c')。
十二、小结与延伸阅读
- lambda 只写一个表达式,一般用来传给别的函数;
- 高阶函数:map / filter / reduce / sorted / min / max / any / all;
- 现代 Python 更爱推导式,除非用现成函数;
operator.itemgetter/attrgetter是 sort key 的最佳选择;functools.partial用来”预配置”函数;- 函数是一等公民 → 可以进容器、传参、返回;
- 别过度函数式,能推导式就推导式。
延伸阅读:
- functools 模块:https://docs.python.org/zh-cn/3/library/functools.html
- operator 模块:https://docs.python.org/zh-cn/3/library/operator.html
- PEP 8 lambda 规范:https://peps.python.org/pep-0008/#programming-recommendations
下一篇 装饰器入门 我们把”高阶函数 + 闭包”合体,写出真正的 Pythonic 装饰器。