定义
functools是Python标准库提供的一个模块,用于高阶函数与可调用对象上的操作。所谓高阶函数,是指那些作用于其他函数或者返回其他函数的函数;就本模块的用途而言,任何可调用对象都可以当作函数来处理[1][5]。
该模块不引入新的语法,而是以普通函数和装饰器的形式给出工具,因此可以配合装饰器、闭包等既有机制使用[3]。其中partial用于实现部分函数应用,reduce用于把可迭代对象归约为单个值,lru_cache与cache用于记忆化,singledispatch用于把函数改造成单分派泛型函数,wraps与update_wrapper则服务于装饰器的编写[1][6]。
原理
偏函数应用的基本思路是固定原函数的一部分参数,得到一个参数更少的新可调用对象。functools.partial返回的partial对象保存原始函数、已绑定的位置参数和关键字参数;此后调用该对象时,新传入的位置参数会接在已绑定参数之后,新传入的关键字参数则会覆盖原有同名项[7]。
记忆化缓存依据参数查找结果。以lru_cache为例,被包装的函数附带cache_info()、cache_clear()与cache_parameters()等接口,可分别查看命中与未命中情况、清空缓存、读取配置;原始函数可通过__wrapped__属性访问,便于内省或绕过缓存重新装饰[1]。由于底层用字典保存结果,传给被包装函数的位置参数和关键字参数必须可哈希,否则无法作为缓存键[1]。
缓存淘汰采用最近最少使用(LRU)策略,其有效性依赖于近期的调用能够较好地预测接下来的调用,例如新闻站点上最受关注的文章往往逐日变化;缓存容量上限则避免长时间运行的进程无限占用内存[1][2]。归约操作按从左到右的顺序进行,reduce把两参数函数反复施加到序列元素上,左参数为累积值,右参数为当前元素,并可选的initial作为初始值参与计算[5]。
单分派泛型函数按照第一个参数的类型选择实现。用singledispatch装饰的函数是面向object类型的默认实现,为其类型注册的具体实现通过register()属性加入;找不到匹配类型时,按方法解析顺序回溯到更通用的实现,已注册实现的查询结果会被缓存[8][9]。
发展历程
该模块的起点是PEP 309。2003年2月8日,Peter Harris提交了关于部分函数应用的提案,最初建议的标准库模块名为functional;提案被采纳并实施后,社区希望收纳更多作用于函数对象、但与函数式编程无直接关系的工具,于是模块改名为functools,以体现其被拓宽的定位[7][10]。相关补丁在2005年被接受并应用于Python 2.5,使partial、update_wrapper和wraps进入标准库[7][11]。
进入Python 3时代后,模块内容持续增加。cumulative影响较大的变化是reduce的迁入:它原本是Python 2的内置函数,在Python 3中移入functools,并回溯到Python 2.6[12][4]。随后cmp_to_key与total_ordering在3.2版加入,lru_cache同样出现在3.2版,并在3.3版增加typed选项[4]。
后续版本继续补齐工具。单分派泛型函数由PEP 443提出,作者为Łukasz Langa,随Python 3.4进入functools;其功能此前已作为pkgutil中的simplegeneric存在,因此实现基本是整体迁移[9][8]。cached_property在3.8版加入,用于把类方法转换为只计算一次、随后作为普通属性缓存的值;无界的cache装饰器在3.9版加入[5]。
应用
在装饰器编写中,wraps与update_wrapper常被用来让包装函数保留原函数的名称、模块名与文档字符串;若不更新这些元数据,返回函数的属性会反映包装层的定义,而非被装饰函数的定义。
偏函数应用适合在需要更少参数的场景中复用函数。例如把int的base参数固定为2,可以得到一个把二进制字符串转为整数的可调用对象;也可以固定某个乘数,构造出行为类似乘2或乘方的函数[2][6][13]。
缓存工具多用于递归计算与重复的高开销调用。官方文档给出的例子包括用lru_cache配合动态规划计算斐波那契数列,以及对静态网络内容获取函数设定缓存容量,从而避免反复请求[1]。
比较函数转换与类型分派各有适用场合。cmp_to_key把接受两个参数、返回负数、零或正数的旧式比较函数,转换为只接受一个参数并返回排序键的键函数,可配合sorted()、min()、max()等工具使用,主要用于从Python 2迁移过来的代码[2][14]。singledispatch则让同一操作针对不同类型提供不同实现,调用时按首个参数类型自动选取[9]。
局限
缓存类工具并不适用于所有函数。用lru_cache缓存带有副作用的函数、每次调用都需创建不同可变对象的函数(如生成器与异步函数),或者time()、random()这类不纯的函数,都没有实际意义[1]。缓存还会一直持有参数与返回值的引用,直到它们离开缓存或被清空[1]。
多线程下的行为需要留意。lru_cache的缓存是线程安全的,底层数据结构在并发更新期间保持一致,但若另一线程在首次调用完成并写入缓存之前发起额外调用,被包装函数可能被多次执行;在较早的文档中,多线程环境下命中数与未命中数被描述为近似值[1]。
cached_property存在自身的边界。它无法防止多线程使用中出现的竞争条件,取值函数可能在同一实例上运行多次,因此适合取值幂等的情形,如需同步应由使用者在取值函数内部或属性访问处自行加锁;它还会干扰PEP 412键共享字典,可能使实例字典占用更多空间,并且要求实例的__dict__为可变映射,因而对元类以及定义了__slots__而不含__dict__的类型不适用[5]。
参数与分派方面同样有限制。缓存键要求参数可哈希,且参数书写模式不同可能被视作不同调用,例如f(a=1,b=2)与f(b=2,a=1)可能各占一个缓存项;类型区分只作用于直接参数而不涉及其内部内容[1]。singledispatch只依据第一个参数的类型分派,注册表也并非线程安全,面对通过ABC注册造成的歧义分派会直接抛出RuntimeError而不作猜测[9][8]。
参见
参考资料
- functools —— 高阶函数,以及可调用对象上的操作 [引用日期2026-10-14]
- functools [引用日期2026-10-14]
- functools | Python Standard Library – Real Python [引用日期2026-10-14]
- A HITCHHIKER'S GUIDE TO functools [引用日期2026-10-14]
- functools --- 可呼叫物件上的高階函式與操作¶ [引用日期2026-10-14]
- Python Functools Module - Python Cheat Sheet [引用日期2026-10-14]
- PEP 309 -- Partial Function Application [引用日期2026-10-14]
- python.org 上的网页 [引用日期2026-10-14]
- PEP 443 – 单分派泛型函数 [引用日期2026-10-14]
- PEP 309 – 部分函数应用 | peps.python.org [引用日期2026-10-14]
- python.org 上的网页 [引用日期2026-10-14]
- 【使用Python实现算法】05 标准库(函数式编程模块) [引用日期2026-10-14]
- functools,一个好用的 python 工具! [引用日期2026-10-14]
- functools [引用日期2026-10-14]