Python中functools 模块的使用小结

 更新时间:2026年07月05日 14:48:10   作者:Csvn  
functools是 Python 标准库中专门为高阶函数服务的模块,内置了一系列用于函数式编程和装饰器开发的实用工具,本文就来详细的介绍一下functools 模块,感兴趣的可以了解一下

知识点简介

functools 是 Python 标准库中专门为高阶函数服务的模块,内置了一系列用于函数式编程和装饰器开发的实用工具。掌握 functools 能让你写出更简洁、更 Pythonic、性能更好的代码。本文覆盖最常用的 4 个工具:@wraps@lru_cachepartial@singledispatch

核心工具详解

1. @functools.wraps — 保留元信息的装饰器

写装饰器时,被装饰的函数会丢失原名、文档字符串、注解等信息。@wraps 负责把这些元信息拷贝回来。

import functools

def log_call(func):
    @functools.wraps(func)  # 保留 func 的 __name__, __doc__ 等
    def wrapper(*args, **kwargs):
        print(f"调用 {func.__name__}")
        return func(*args, **kwargs)
    return wrapper

@log_call
def greet(name: str) -> str:
    """向某人打招呼"""
    return f"你好, {name}"

print(greet.__name__)  # greet(没有 @wraps 会返回 wrapper)
print(greet.__doc__)   # 向某人打招呼
print(greet.__annotations__)  # {'name': <class 'str'>, 'return': <class 'str'>}

2. @functools.lru_cache — 记忆化缓存

自动缓存函数的返回值,相同参数再次调用直接返回缓存结果。适合纯函数和无副作用的递归或计算密集型操作。

import functools
import time

@functools.lru_cache(maxsize=128)
def fib(n: int) -> int:
    """带缓存的斐波那契数列"""
    if n < 2:
        return n
    return fib(n - 1) + fib(n - 2)

start = time.time()
print(fib(100))  # 354224848179261915075
print(f"耗时: {time.time() - start:.6f}s")  # 瞬间完成

# 查看缓存统计
print(fib.cache_info())  # CacheInfo(hits=98, misses=101, maxsize=128, currsize=101)

注意:@lru_cache 的参数必须是可哈希的。如果参数是 list、dict 等可变类型,需要先转成 tuple 或使用 @cache(Python 3.9+)。

3. functools.partial — 偏函数冻结

固定一个函数的某些参数,生成一个新函数。适用于回调、配置化场景。

import functools

def power(base: float, exp: float) -> float:
    return base ** exp

# 创建平方和立方偏函数
square = functools.partial(power, exp=2)
cube = functools.partial(power, exp=3)

print(square(5))   # 25
print(cube(5))     # 125

# 在排序中固定 key 函数
data = [(1, "z"), (3, "a"), (2, "m")]
sorted(data, key=functools.partial(lambda k, i: k[i], i=1))
# 按第二个元素排序: [(3, 'a'), (2, 'm'), (1, 'z')]

4. @functools.singledispatch — 单分派泛函数

根据第一个参数的类型执行不同的函数体。替代大量 if/elif isinstance(...) 的链式判断。

import functools

@functools.singledispatch
def serialize(obj):
    """将对象序列化为字符串"""
    raise TypeError(f"不支持的类型: {type(obj)}")

@serialize.register(int)
def _(obj: int) -> str:
    return f"数字: {obj}"

@serialize.register(str)
def _(obj: str) -> str:
    return f"字符串: '{obj}'"

@serialize.register(list)
def _(obj: list) -> str:
    return f"列表: {[serialize(x) for x in obj]}"

@serialize.register(dict)
def _(obj: dict) -> str:
    return f"字典: {{{', '.join(f'{k!r}: {v}' for k, v in obj.items())}}}"

# 测试
print(serialize(42))         # 数字: 42
print(serialize("hello"))    # 字符串: 'hello'
print(serialize([1, "a"]))   # 列表: ['数字: 1', "字符串: 'a'"]

综合使用示例

import functools
import time
from typing import Any

def timer_and_cache(func=None, *, maxsize: int = 128):
    """组合了计时和缓存的装饰器工厂"""
    def decorator(f):
        @functools.wraps(f)
        @functools.lru_cache(maxsize=maxsize)
        def wrapper(*args, **kwargs):
            start = time.perf_counter()
            result = f(*args, **kwargs)
            elapsed = time.perf_counter() - start
            print(f"{f.__name__} 耗时 {elapsed:.6f}s")
            return result
        wrapper.original = f  # 保留原始函数引用
        return wrapper
    return decorator(func) if func else decorator

@timer_and_cache
def compute(n: int) -> int:
    """模拟复杂计算"""
    time.sleep(0.5)
    return n * n

print(compute(10))   # 首次 500ms+
print(compute(10))   # 缓存命中,瞬间返回
print(compute.cache_info())  # CacheInfo(hits=1, misses=1, ...)

注意事项 / 避坑指南

  • @lru_cache 不要装饰有副作用的方法:如果函数依赖外部状态(文件、数据库、全局变量),缓存会导致返回过期结果。
  • @wraps 不是万能的:它仅拷贝 __module__、__name__、__qualname__、__annotations__、__doc__ 和 __dict__。签名信息(inspect.signature)不会自动恢复,如需保留完整签名配合 @decorator 库或 inspect 手动处理。
  • partial 对象不是普通函数:functools.partial 返回的对象没有 __name__ 属性(除非手动设置),也不支持注解。检查类型时用 callable() 而非 isfunction()。
  • singledispatch 只对第一个参数生效:如果需要根据多个参数分派,考虑 functools.singledispatchmethod(Python 3.8+)或使用类型模式匹配(Python 3.10+)。
  • 缓存泄漏内存:@lru_cache(maxsize=None) 会无限增长,慎用。对于预期大量不同参数调用的场景,设置合理的 maxsize 或考虑 @cache + 手动清除策略。

到此这篇关于Python中functools 模块的使用小结的文章就介绍到这了,更多相关Python functools 模块内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • django中使用事务及接入支付宝支付功能

    django中使用事务及接入支付宝支付功能

    这篇文章主要介绍了django中使用事务以及接入支付宝支付功能,本文通过实例代码给大家介绍的非常详细,具有一定的参考借鉴价值,需要的朋友可以参考下
    2019-09-09
  • 用matplotlib画等高线图详解

    用matplotlib画等高线图详解

    这篇文章主要介绍了用matplotlib画等高线图详解,具有一定借鉴价值,需要的朋友可以参考下。
    2017-12-12
  • Python word2vec训练词向量实例分析讲解

    Python word2vec训练词向量实例分析讲解

    这篇文章主要介绍了Python word2vec训练词向量实例分析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习吧
    2022-12-12
  • PyQt弹出式对话框的常用方法及标准按钮类型

    PyQt弹出式对话框的常用方法及标准按钮类型

    这篇文章主要为大家详细介绍了PyQt弹出式对话框的常用方法及标准按钮类型,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2019-02-02
  • Python图像增强imgaug详解

    Python图像增强imgaug详解

    Imgaug 是一个开源 python 包,可让你在机器学习实验中增强图像,它适用于各种增强技术,它有一个简单而强大的界面,可以增强图像、地标、边界框、热图和分割图,这篇文章主要介绍了Python图像增强imgaug,需要的朋友可以参考下
    2022-11-11
  • Python进程间通信之共享内存详解

    Python进程间通信之共享内存详解

    这篇文章主要为大家详细介绍了Python进程间通信之共享内存的相关资料,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2017-10-10
  • python dict 相同key 合并value的实例

    python dict 相同key 合并value的实例

    今天小编就为大家分享一篇python dict 相同key 合并value的实例,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2019-01-01
  • Python牛刀小试密码爆破

    Python牛刀小试密码爆破

    用Python与vbs分别实现的爆破bbs论坛账号的代码,学习python或vbs的朋友可以参考下。
    2011-02-02
  • python中break、continue 、exit() 、pass终止循环的区别详解

    python中break、continue 、exit() 、pass终止循环的区别详解

    这篇文章主要介绍了python中break、continue 、exit() 、pass终止循环的区别详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2019-07-07
  • python sorted()函数的key参数使用说明

    python sorted()函数的key参数使用说明

    文章介绍了Python中的sort()和sorted()方法的区别及其用法,sort()是list的方法,不能对所有可迭代对象进行排序,且在原地对列表排序,无返回值,sorted()是内置函数,可以对所有可迭代对象进行排序,并返回一个新的排序后的列表,文章还提供了sorted()的用法示例
    2025-11-11

最新评论