functools 是 Python 标准库中专门处理高阶函数(操作或返回函数的函数)的模块。它提供了一系列工具,让你能更灵活地创建、修改和优化函数。日常开发中最常用的是偏函数、函数缓存和装饰器相关工具。
偏函数 partial:给函数“预填”参数
functools.partial 可以固定一个函数的部分参数,生成一个新函数。这在你需要多次调用同一个函数、且某些参数基本不变时特别有用,能减少重复代码,也让接口更清晰。
from functools import partial
# 原始函数:计算 x 的 n 次幂
def power(base, exponent):
return base ** exponent
# 用 partial 固定 exponent=2,生成“平方”函数
square = partial(power, exponent=2)
cube = partial(power, exponent=3)
print(square(5)) # 25
print(cube(5)) # 125
典型场景:
- 回调函数参数适配:比如 GUI 按钮点击时回调只有一个参数,但你需要传递额外信息,就可以用
partial绑定。 - 简化复杂接口:当某个库函数的参数很多,而你的业务中大部分参数都是固定值,可以创建一个
partial版本,只暴露业务关心的参数。 - 配合
map/sorted:比如对列表排序时指定一个固定的key提取函数,可以用partial快速生成。
函数缓存 lru_cache / cache:避免重复计算
当某个函数被频繁调用、计算代价较高、且输入相同输出也相同时,缓存它的返回值可以显著提升性能。functools.lru_cache 提供了一个基于 LRU(最近最少使用)策略的缓存。
from functools import lru_cache
# 一个计算斐波那契数的递归函数(效率极低,因为重复计算)
# 加上缓存后性能暴涨
@lru_cache(maxsize=128) # maxsize 控制缓存大小,None 表示不限制
def fib(n):
if n < 2:
return n
return fib(n-1) + fib(n-2)
print(fib(50)) # 不加缓存几乎跑不动,加了瞬间出结果
重要参数与变体:
maxsize:缓存最大条目数。设为None则缓存无限增长(Python 3.8+ 可以使用@cache装饰器,相当于@lru_cache(maxsize=None))。typed:设为True时,不同参数类型的相同值会分开缓存(例如f(3)和f(3.0)不共享缓存)。- 缓存穿透与失效:缓存是
内存中的,进程结束就消失。如果需要持久化缓存,得另找方案(如diskcache)。
实用建议:
- 缓存纯函数(相同输入永远返回相同输出,无副作用)效果最好,比如复杂计算、数据库查询结果(当数据不常变时)。
- 不要滥用:缓存会占用额外内存,且对于参数组合极多、每次调用参数几乎都不同的函数,缓存反而增加开销。
- 可以用
func.cache_info()查看缓存命中率,帮助调优。
高阶函数工具:reduce、wraps 等
虽然 map、filter 已经内置,但 reduce(逐元素累积值)被放到了 functools 里,体现了“少用”的理念,但该用的时候依然方便。
from functools import reduce
# 计算列表中所有数字的乘积
nums = [1, 2, 3, 4]
product = reduce(lambda x, y: x*y, nums) # 24
# 也可以给初始值
product_with_initial = reduce(lambda x, y: x*y, nums, 10) # 240
不过,在实际开发中,能不用 reduce 就尽量用内置函数(如 sum、math.prod)或显式的循环,因为可读性更好。只有在确实需要自定义累积逻辑时才搬它。
装饰器必备:wraps
写装饰器时,如果不加 functools.wraps,原始函数的元信息(name、doc 等)会被装饰器内部函数覆盖。这会导致调试困难和不好看的日志输出。因此写装饰器,第一个要加的就是 @wraps(func)。
from functools import wraps
def log(func):
@wraps(func) # 保留原始函数的名字和文档
def wrapper(*args, **kwargs):
print(f"调用 {func.__name__}")
return func(*args, **kwargs)
return wrapper
@log
def add(a, b):
"""返回 a + b"""
return a + b
print(add.__name__) # add,如果没有 @wraps 会是 wrapper
print(add.__doc__) # 返回 a + b
其他值得一提的工具
total_ordering:用在自定义类上,只需定义eq和另外一个魔法方法(如lt),它会自动补全所有比较方法(le、gt、ge)。减少样板代码。singledispatch:实现单分派的泛型函数,可以根据第一个参数的类型,调用不同的函数实现。用于编写对不同数据类型有不同处理逻辑的库时很干净。cached_property:在类中,将方法变成属性,计算结果只计算一次并缓存为实例属性。常用来替代惰性计算场景下反复调用的计算属性。
小结:functools 提供的工具虽然不多,但每个都很实用。日常高频出现的是 partial 简化函数调用、lru_cache 优化耗时函数、wraps 写出地道的装饰器。掌握它们能让你的 Python 代码更简洁、更高效。