Python函数式编程
Python 函数式编程的四大件:推导式、高阶函数、装饰器、生成器。它们都围绕一个核心思想——把"对集合做什么"表达成声明式的变换,而不是命令式地写循环。函数是一等公民,可以赋值、传参、返回,这让 map/filter/sorted 等高阶函数有了用武之地;装饰器则是高阶函数的语法化,几乎每个 Python 框架都在用它。
推导式(Comprehension)
推导式用一行表达式生成新集合,替代 for 循环 + append 的三行样板。三种容器各有对应:
# 列表推导式:筛选 + 变换一步完成
squares = [x * x for x in range(10) if x % 2 == 0] # [0, 4, 16, 36, 64]
# 字典推导式
d = {k: len(k) for k in ["a", "bb", "ccc"]} # {'a':1, 'bb':2, 'ccc':3}
# 集合推导式(自动去重)
s = {x % 3 for x in range(10)} # {0, 1, 2}推导式后面的 if 是过滤器,前面的表达式是变换——顺序固定,不要写反。生成器表达式(把 [] 换成 ())惰性求值,不一次生成全部元素:
total = sum(x * x for x in range(10)) # 惰性,省内存高阶函数
接受函数作为参数或返回函数的函数叫高阶函数,map/filter/sorted/reduce 是最常用的一批:
| 函数 | 作用 | 示例 |
|---|---|---|
map(f, it) | 对每个元素应用 f | list(map(str, [1,2])) → ['1','2'] |
filter(pred, it) | 保留满足条件的元素 | list(filter(lambda x: x>1, [0,1,2])) → [2] |
sorted(it, key=...) | 按 key 排序 | sorted([(1,'b'),(2,'a')], key=lambda t: t[1]) |
reduce(f, it) | 累积归约 | reduce(lambda a,b: a+b, [1,2,3]) → 6 |
from functools import reduce
# 推导式通常比 map/filter 更 Pythonic,二者可互换
[x * 2 for x in range(5)] # 列表推导式
list(map(lambda x: x * 2, range(5))) # 等价 map 写法优先推导式
现代 Python 惯例:能用推导式就不用 map/filter(可读性更好);reduce 因可读性差,官方已边缘化,简单场景优先 sum()/min()/max()。
装饰器
装饰器是"给函数套一层外壳"的语法糖——在不改函数源码的前提下,给它附加日志、计时、鉴权、缓存等横切逻辑。
import time
def timer(func):
def wrapper(*args, **kwargs):
start = time.perf_counter()
result = func(*args, **kwargs)
print(f"{func.__name__} 耗时 {time.perf_counter() - start:.4f}s")
return result
return wrapper
@timer # 等价于 add = timer(add)
def add(a, b):
time.sleep(0.1)
return a + b
add(1, 2) # 打印 "add 耗时 0.1000s",返回 3装饰器能工作,靠的是闭包——wrapper 记住了 func。*args/**kwargs 通配任意参数,让装饰器通用。经典案例:@property 把方法变属性、@staticmethod 标记静态方法、Flask 的 @app.route() 注册路由、pytest 的 @pytest.fixture() 声明夹具。
生成器
生成器用 yield 实现惰性求值——按需产生下一个值,内存占用从 O(n) 降到 O(1)。处理大文件、数据库游标、API 分页时是标准做法。
def read_large(path):
with open(path) as f:
for line in f:
yield line.strip() # 一次只产出一行
for line in read_large("data.txt"):
print(line)生成器有两种写法:yield 函数和生成器表达式。生成器只能遍历一次,遍历完就耗尽——这是与列表最大的差异。
gen = (x * 2 for x in range(3))
list(gen) # [0, 2, 4]
list(gen) # [] ← 已耗尽生成器 vs 列表
数据量小用列表(可重复遍历、支持下标);数据量大或无限序列用生成器。判断标准:你是否真的需要同时持有全部元素。
生成器与闭包、装饰器共同构成 Python 函数式工具箱,配合 Python语法 的数据类型与函数机制使用。