当前位置:首页>python>Python functools 全解析:lru_cache、partial、wraps 让代码更优雅

Python functools 全解析:lru_cache、partial、wraps 让代码更优雅

  • 2026-10-11 08:35:55
Python functools 全解析:lru_cache、partial、wraps 让代码更优雅

字数 1149,阅读大约需 6 分钟

Python functools 全解析

写Python代码久了,你肯定会遇到这种场景:一个函数计算量很大,每次调用都像在等外卖;某个函数参数太多,调用时总得查文档;写个装饰器,结果原函数的 __name__ 和 __doc__ 全丢了。

这些问题,Python 标准库 functools 全帮你想到了解决方案。今天就来聊聊这个让代码更优雅的「瑞士军刀」。

lru_cache:给你的函数加个缓存

先看一个经典的「性能杀手」——递归斐波那契数列:



1
2
3
4
5
6

def fib(n):
    if n <= 1:
        return n
    return fib(n-1) + fib(n-2)

print(fib(35))  # 等吧,能等到你怀疑人生


为什么慢?因为 fib(35) 会重复计算 fib(33) 和 fib(34),而它们又各自重复计算更小的值……复杂度直接爆炸到 O(2^n)。

加一行装饰器:



1
2
3
4
5
6
7
8
9

from functools import lru_cache

@lru_cache(maxsize=128)
def fib(n):
    if n <= 1:
        return n
    return fib(n-1) + fib(n-2)

print(fib(100))  # 瞬间出结果


lru_cache 的原理很简单:把函数的调用结果缓存起来,下次遇到相同参数直接返回缓存值。LRU 的意思是「最近最少使用」,当缓存满了,就淘汰最久没用的那条。

几个实用参数:

  • • maxsize=None:缓存无限大(慎用,可能导致内存爆炸)
  • • maxsize=128:默认值,适合大多数场景
  • • @lru_cache 不加参数:等同于 maxsize=128

注意:被缓存的函数参数必须是可哈希的(不能是列表、字典等可变类型),否则报错。

partial:固定参数,减少调用成本

有些函数参数太多,每次调用都要传一串相同的值。比如你要用 int 函数把一堆字符串转成十六进制整数:



1
2

data = ['1a', 'ff', 'dead', 'beef']
result = [int(x, 16) for x in data]  # 每次都要写 16


用 partial 把 base=16 固定住:



1
2
3
4

from functools import partial

hex_to_int = partial(int, base=16)
result = [hex_to_int(x) for x in data]  # 干净多了


更实用的场景是回调函数。假设你有个带超时参数的网络请求函数:



1
2
3

def fetch(url, timeout, retry):
    # 网络请求逻辑
    pass


在某个场景下,你希望所有请求都用相同的超时和重试次数:



1
2

quick_fetch = partial(fetch, timeout=5, retry=1)
quick_fetch('https://api.example.com/data')


partial 创建的是一个 partial 对象,行为像函数,但你可以用 .func 查看原函数,用 .args 和 .keywords 查看绑定的参数。

wraps:装饰器的好搭档

写装饰器时,你可能会发现被装饰的函数「身份丢失」了:



1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

def my_decorator(func):
    def wrapper(*args, **kwargs):
        print('调用前')
        result = func(*args, **kwargs)
        print('调用后')
        return result
    return wrapper

@my_decorator
def greet(name):
    """打招呼函数"""
    print(f'Hello, {name}!')

print(greet.__name__)  # 输出 wrapper,不是 greet
print(greet.__doc__)   # 输出 None,原函数的文档丢了


这在调试、写文档时非常麻烦。wraps 就是来解决这个问题的:



1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

from functools import wraps

def my_decorator(func):
    @wraps(func)
    def wrapper(*args, **kwargs):
        print('调用前')
        result = func(*args, **kwargs)
        print('调用后')
        return result
    return wrapper

@my_decorator
def greet(name):
    """打招呼函数"""
    print(f'Hello, {name}!')

print(greet.__name__)  # 输出 greet
print(greet.__doc__)   # 输出「打招呼函数」


wraps 做的事情很简单:把原函数的 __name__、__doc__、__module__、__annotations__ 等属性复制到 wrapper 函数上。这样,被装饰的函数就「看起来」还是原来的函数。

reduce:老朋友的新家

如果你用过 Python 2,可能记得 reduce 曾经是内置函数。Python 3 把它移到了 functools 里。



1
2
3
4
5

from functools import reduce

nums = [1, 2, 3, 4, 5]
total = reduce(lambda x, y: x + y, nums)
print(total)  # 15


当然,求和用 sum() 更简洁,但 reduce 更通用,可以处理任意二元累积操作:



1
2
3
4
5

# 连乘
product = reduce(lambda x, y: x * y, nums)

# 找最大值
maximum = reduce(lambda x, y: x if x > y else y, nums)


cmp_to_key:自定义排序的救星

Python 的 sorted 函数用 key 参数处理排序,但有时你已经有了一个比较函数(返回 -1/0/1),不想重写成 key 的形式。

cmp_to_key 可以把比较函数转成 key 函数:



1
2
3
4
5
6
7
8
9
10
11
12
13

from functools import cmp_to_key

def compare(a, b):
    # 自定义比较逻辑
    if len(a) > len(b):
        return 1
    elif len(a) < len(b):
        return -1
    return 0

words = ['apple', 'pie', 'banana', 'a']
sorted_words = sorted(words, key=cmp_to_key(compare))
print(sorted_words)  # ['a', 'pie', 'apple', 'banana']


总结

functools 这个模块虽然名字低调,但里面全是实用工具:

  • • lru_cache:缓存函数结果,性能提升利器
  • • partial:固定参数,简化函数调用
  • • wraps:写装饰器时保持函数「身份」
  • • reduce:累积操作的通用工具
  • • cmp_to_key:老式比较函数的适配器

下次写代码遇到「性能差、参数多、装饰器丢信息」这些问题时,记得先想想 functools 有没有现成的解决方案。标准库里藏着不少宝藏,就看你有没有耐心去挖了。

你还知道哪些好的编程学习方法?欢迎在评论区分享!

开发必备神器

  1. 强大的编程神器:IntelliJ IDEA 免费激活

  2. 轻松安装 MySQL 数据库

  3. 数据库管理神器 - Navicat 的奇妙之旅

  4. 宝藏级的开发手册,前后端开发语言都有,自己也在用

·················END·················

最新文章

随机文章