当前位置:首页>python>Python 进阶利器:详解 functools 模块常用方法

Python 进阶利器:详解 functools 模块常用方法

  • 2026-09-06 16:02:11
Python 进阶利器:详解 functools 模块常用方法

在 Python 的标准库中,有一个专门为函数式编程(Functional Programming)设计的模块——functools。它提供了一系列高阶函数(Higher-order functions),即“作用于函数”或“返回函数”的函数。

熟练掌握 functools,不仅能让你的代码更加简洁、优雅,还能显著提升程序性能。本文将通过实际代码示例,详细解读该模块中最常用的几个工具。

1. @lru_cache:性能加速器(缓存)

这是 functools 中最受欢迎的功能之一。全称为 "Least Recently Used Cache"(最近最少使用缓存)。

作用:它能自动把函数的输入和输出结果缓存起来。当下次以相同的参数调用该函数时,直接从内存返回结果,而不再重新计算。非常适合耗时的计算或递归操作。

示例:斐波那契数列

如果不加缓存,递归计算斐波那契数列会有大量的重复计算,效率极低。

from functools import lru_cacheimport time# maxsize=None 表示缓存大小无限,默认为 128@lru_cache(maxsize=None)deffib(n):if n < 2:return nreturn fib(n-1) + fib(n-2)start = time.time()print(f"结果: {fib(35)}")  # 瞬间完成print(f"耗时: {time.time() - start:.4f}秒")# 查看缓存命中情况print(fib.cache_info())# 输出示例: CacheInfo(hits=33, misses=36, maxsize=None, currsize=36)

注意:

  • 自 Python 3.9 起,可以使用 @functools.cache,它是 @lru_cache(maxsize=None) 的简化版。
  • 被缓存函数的参数必须是可哈希的(hashable),例如列表(list)不能作为参数,但元组(tuple)可以。

2. partial:偏函数(参数冻结)

作用:基于一个已有的函数,固定(冻结)其中一部分参数,生成一个新的函数。这样在调用新函数时,只需要传递剩余的参数即可。

场景:当你需要频繁调用某个函数,且其中某些参数总是固定值时,使用 partial 可以简化代码。

示例:定制转换函数

from functools import partial# int() 函数默认按十进制转换,但它接受一个 base 参数print(int('1010', base=2))  # 输出 10# 使用 partial 固定 base=2,创建一个专门处理二进制转换的函数int2 = partial(int, base=2)print(int2('1010'))  # 输出 10print(int2('1111'))  # 输出 15

3. @wraps:装饰器的伴侣

作用:在编写装饰器(Decorator)时,使用 @wraps 可以保留原函数的元数据(如 __name__,__doc__,参数签名等)。

问题:如果不使用 @wraps,被装饰后的函数其实变成了装饰器内部的 wrapper 函数,这会导致调试困难,且无法获取原函数的文档字符串。

示例:标准的装饰器写法

from functools import wrapsdefmy_logger(func):    @wraps(func)  # 关键行:保留原函数 func 的元数据defwrapper(*args, **kwargs):        print(f"正在执行函数: {func.__name__}")return func(*args, **kwargs)return wrapper@my_loggerdefadd(x, y):"""这是一个加法函数"""return x + yprint(add(1, 2))print(f"函数名: {add.__name__}")  # 输出: add (如果没有 @wraps,这里会输出 wrapper)print(f"文档: {add.__doc__}")    # 输出: 这是一个加法函数

4. reduce:累积计算

作用:将一个二元函数(接收两个参数)作用于序列的元素,从左到右进行累积运算,最终合并为一个值。

注:在 Python 2 中它是全局函数,Python 3 中移到了 functools 模块。

示例:累乘(阶乘)

from functools import reducedata = [1, 2, 3, 4, 5]# 计算过程:((((1*2)*3)*4)*5)result = reduce(lambda x, y: x * y, data)print(result) # 输出 120

虽然 sum() 可以做加法,但在处理更复杂的累积逻辑(如合并字典、路径拼接)时,reduce 非常强大。

5. @singledispatch:单分发泛型函数

作用:它允许你根据第一个参数的类型,让同一个函数表现出不同的行为。这类似于其他语言中的函数重载(Overloading),但更加 Pythonic,避免了大量的 if isinstance(...) 判断。

示例:根据数据类型格式化输出

from functools import singledispatch@singledispatchdefformat_data(data):"""默认处理逻辑"""    print(f"未知类型: {data}")@format_data.register(str)def_(data):    print(f"处理字符串: {data.upper()}")@format_data.register(list)def_(data):    print(f"处理列表,长度为: {len(data)}")@format_data.register(int)def_(data):    print(f"处理整数: {data * 10}")# 调用同一个函数,行为不同format_data("hello")  # 处理字符串: HELLOformat_data([1, 2, 3])# 处理列表,长度为: 3format_data(42)       # 处理整数: 420format_data(1.5)      # 未知类型: 1.5

6. @cached_property:惰性属性(Python 3.8+)

作用:将类的方法转换为属性,并且只计算一次。计算结果会被存储在实例的 __dict__ 中,之后的调用直接读取属性值。

场景:适用于计算成本高昂(如读取大文件、复杂运算),且在实例生命周期内结果不变的属性。

示例:数据集加载

from functools import cached_propertyimport timeclassDataSet:def__init__(self, filename):        self.filename = filename    @cached_propertydefdata(self):        print("正在加载巨型数据...")        time.sleep(2)  # 模拟耗时操作return {"id": 1, "content": "Big Data"}ds = DataSet("file.csv")print("对象已创建")# 第一次访问,会执行耗时操作print(ds.data) # 第二次访问,直接从内存获取,无延迟print(ds.data) 

7. @total_ordering:补全比较方法

作用:在定义类时,如果你定义了 __eq__ 和以下四个比较方法之一(__lt__, __le__, __gt__, __ge__),使用该装饰器可以自动补全剩下的比较方法。

场景:简化自定义对象的排序逻辑代码。

示例:学生成绩比较

from functools import total_ordering@total_orderingclassStudent:def__init__(self, name, score):        self.name = name        self.score = scoredef__eq__(self, other):return self.score == other.score# 只需要定义小于 (__lt__),total_ordering 会自动补全 >、<=、>=def__lt__(self, other):return self.score < other.scores1 = Student("Alice", 85)s2 = Student("Bob", 90)print(s1 < s2)  # True (手动定义的)print(s1 > s2)  # False (自动补全的)print(s1 <= s2) # True (自动补全的)

总结

functools 是 Python 中极具生产力的模块。

  1. 想优化性能? 用 @lru_cache 或 @cached_property。
  2. 想简化函数调用? 用 partial。
  3. 写装饰器? 务必加上 @wraps。
  4. 想写更清晰的类型判断逻辑? 用 @singledispatch。
  5. 处理数据累积? 用 reduce。

掌握这些工具,能让你从繁琐的样板代码中解放出来,专注于核心逻辑的实现。

最新文章

随机文章