J'Blog

20 课 · 进阶

语义检索

基于 Chroma 向量库,与 Workspace 知识库分离。用自然语言描述你想查的内容。 非管理员每人每天可检索 5 次(消耗站点 AI 配额)。

functools 高阶函数与装饰器

原文:functools 与装饰器

一句话

提供高阶函数和可调用对象操作,实现函数缓存、部分应用和泛型分发。

什么时候翻这页

  • 需要缓存函数结果以提高性能时
  • 创建可复用的工具函数,特别是Agent调用的工具
  • 实现多类型参数处理的泛型函数时
  • 需要保留原始函数元数据的装饰器开发时

核心概念

  • 高阶函数:处理或返回其他函数的函数
  • 函数缓存:存储函数计算结果,避免重复计算
  • 部分应用:固定函数的部分参数,创建新函数
  • 泛型函数:根据参数类型分发到不同实现
  • 装饰器:修改或增强函数行为的工具

怎么做

缓存函数结果

# 使用lru_cache缓存函数结果
from functools import lru_cache

@lru_cache(maxsize=32)
def get_pep(num):
    'Retrieve text of a Python Enhancement Proposal'
    resource = f'https://peps.python.org/pep-{num:04d}'
    try:
        with urllib.request.urlopen(resource) as s:
            return s.read()
    except urllib.error.HTTPError:
        return 'Not Found'

部分应用函数

# 创建部分应用的函数
from functools import partial

# 创建二进制转换函数
basetwo = partial(int, base=2)
basetwo('10010')  # 返回 18

# 使用占位符创建灵活的部分函数
from functools import partial, Placeholder as _
remove = partial(str.replace, _, _, '')
remove('Hello, dear dear world!', ' dear')  # 返回 'Hello, world!'

创建泛型函数

# 创建单分派泛型函数
from functools import singledispatch

@singledispatch
def fun(arg, verbose=False):
    if verbose:
        print("Let me just say,", end=" ")
    print(arg)

# 为不同类型注册不同实现
@fun.register
def _(arg: int, verbose=False):
    if verbose:
        print("Strength in numbers, eh?", end=" ")
    print(arg)

@fun.register
def _(arg: list, verbose=False):
    if verbose:
        print("Enumerate this:")
    for i, elem in enumerate(arg):
        print(i, elem)

保留函数元数据

# 使用wraps保留原始函数的元数据
from functools import wraps

def my_decorator(f):
    @wraps(f)
    def wrapper(*args, **kwds):
        print('Calling decorated function')
        return f(*args, **kwds)
    return wrapper

@my_decorator
def example():
    """Docstring"""
    print('Called example function')

example.__name__  # 返回 'example'
example.__doc__   # 返回 'Docstring'

命令 / API 速查

函数/装饰器描述示例
@cache简单轻量级函数缓存@cache def factorial(n): ...
@lru_cache带大小限制的最近最少使用缓存@lru_cache(maxsize=128) def func(): ...
@cached_property缓存类属性值@cached_property def prop(self): ...
@total_ordering为类实现所有比较方法@total_ordering class MyClass: ...
partial创建部分应用的函数p = partial(func, arg1, kwarg1=val)
partialmethod创建部分应用的方法m = partialmethod(method, arg1)
reduce累积应用函数到序列reduce(lambda x,y: x+y, [1,2,3])
@singledispatch创建单分派泛型函数@singledispatch def func(arg): ...
@singledispatchmethod创建单分派泛型方法@singledispatchmethod def method(self, arg): ...
wraps保留原始函数元数据@wraps(original) def wrapper(): ...

与 Agent 开发的联系

  • 工具缓存:使用@lru_cache缓存Agent工具调用结果,减少重复计算和API调用
  • 工具部分应用:使用partial创建预配置的工具函数,简化Agent工具调用接口
  • 结构化输出:使用@singledispatch根据输入类型分发到不同的输出格式化函数

初学者易错点

  • 缓存装饰器不能用于带有不可哈希参数(如列表、字典)的函数
  • 部分应用函数时,占位符(Placeholder)必须全部填充,否则会引发TypeError
  • 泛型函数的分派基于第一个参数的类型,不是基于参数数量
  • 装饰器会覆盖原始函数的元数据,需要使用@wraps保留
  • 缓存函数可能保持对参数和返回值的引用,导致内存泄漏

相关词条

  • library-itertools 迭代器工具,与functools配合使用
  • library-operator 标准运算符函数,常与functools结合
  • python-decorators Python装饰器模式详解
  • python-closures 闭包与函数工厂
  • python-metaprogramming Python元编程技术

官方原文:https://docs.python.org/zh-cn/3/library/functools.html