partial:固定部分参数

把多参数函数变成"少参数函数":

from functools import partial

def power(base, exp):
    return base ** exp

square = partial(power, exp=2)      # 固定 exp=2
cube   = partial(power, exp=3)

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

实战场景:

import requests
from functools import partial

# 所有 GET 请求都加同一个 header
get = partial(requests.get, headers={"X-Token": "abc"})

get("https://api.example.com/users")
get("https://api.example.com/orders")

lru_cache:函数级缓存

加一行装饰器,重复调用秒返:

from functools import lru_cache

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

fib(100)   # 不缓存的话天荒地老;加了瞬间出

⚠️ 参数必须可哈希(不能是 list / dict)。

清缓存:fib.cache_clear()。看命中率:fib.cache_info()

cache(Python 3.9+):无大小限制

from functools import cache

@cache
def fib(n):
    ...

简化版 lru_cache(maxsize=None)——如果不在意缓存撑大用这个。

wraps:写装饰器必备

不加的话,被装饰的函数会"丢失身份":

from functools import wraps

def log(func):
    @wraps(func)            # 复制 func 的 __name__ / __doc__ 等
    def wrapper(*args, **kwargs):
        print(f"调用 {func.__name__}")
        return func(*args, **kwargs)
    return wrapper

@log
def add(a, b):
    """加法"""
    return a + b

print(add.__name__)    # 'add'  (没加 wraps 会变成 'wrapper')
print(add.__doc__)     # '加法'

写装饰器永远加 @wraps。

reduce

from functools import reduce
reduce(lambda a, b: a + b, [1, 2, 3, 4])    # 10

讲过了,但放进全家桶提一句——它住在 functools 里。

singledispatch:基于类型的函数重载

from functools import singledispatch

@singledispatch
def show(arg):
    print(f"默认: {arg}")

@show.register(int)
def _(arg):
    print(f"整数: {arg * 2}")

@show.register(str)
def _(arg):
    print(f"字符串: {arg.upper()}")

@show.register(list)
def _(arg):
    print(f"列表,长度 {len(arg)}")

show(42)              # 整数: 84
show("hello")         # 字符串: HELLO
show([1, 2, 3])       # 列表,长度 3
show(3.14)            # 默认: 3.14

类似面向对象的"多态",但写法更函数式。

reduce vs sum 的取舍

reduce(lambda a, b: a + b, nums)    # 啰嗦
sum(nums)                            # 简洁,且更快

Python 内置往往比 reduce 更地道——只有真没内置时再用 reduce。

下一篇是装饰器原理(已写)。再下一篇讲装饰器进阶。