首页 > 试题广场 >

你为生产代码添加一个计时装饰器,希望被装饰函数的名称、注解和

[单选题]
你为生产代码添加一个计时装饰器,希望被装饰函数的名称、注解和文档字符串在日志与监控系统中保持不变。最合适的做法是?
  • 在包装函数上使用 functools.wraps(original_func)
  • 在返回前手工赋值 wrapper.__name__、__doc__、__annotations__
  • 用 functools.partial 包装原函数以保留元数据
  • 在装饰器外层再叠加 functools.lru_cache 以保留属性
在 Python 中为装饰器保留原函数元信息(如名称、注解、文档字符串)的最优实践是:正确答案:A. 在包装函数上使用functools.wraps(original_func)

关键解析

1. 选项 A 的正确性

  • functools.wraps的作用通过装饰器语法@functools.wraps(func)应用于包装函数(wrapper),它会自动将原函数func的__name__、__doc__、__annotations__等元信息复制到包装函数中。示例
    def timer(func):
        @functools.wraps(func)  # 关键步骤
        def wrapper(*args, **kwargs):
            # 计时逻辑...
            return func(*args, **kwargs)
        return wrapper
    此时timer装饰的函数会保留原函数的名称、文档和注解。

2. 其他选项的缺陷

  • 选项 B(手工赋值)手动设置wrapper.__name__ = func.__name__等属性虽然可行,但需完整复制所有元信息(如__annotations__、__module__),代码冗余且易遗漏,维护成本高。
  • 选项 C(functools.partial)partial用于固定函数参数生成新函数,与元信息保留无关。例如partial(func, a=1)会创建新函数,但不会继承原函数的元信息。
  • 选项 D(叠加lru_cache)lru_cache是缓存装饰器,与元信息保留无关。叠加使用可能导致缓存逻辑干扰计时功能,且无法解决元信息丢失问题。

生产级实践建议

  1. 必须使用@wraps(func)在装饰器内部对包装函数应用@wraps(func),确保调试、日志、文档生成等场景中函数身份一致性。
  2. 多层装饰器的叠加若存在多层装饰器,需每一层均使用@wraps,否则最外层装饰器会覆盖内层元信息。示例
    @decorator1  # 需包含 @wraps
    @decorator2  # 需包含 @wraps
    def my_func(): ...
  3. 进阶场景扩展
    • 异步函数支持:对async def装饰时,需使用@functools.wraps(func)保持协程特性。
    • 自定义属性保留:通过functools.WRAPPER_ASSIGNMENTS扩展需复制的属性列表。

总结

选项 A 是唯一符合 Python 最佳实践的解决方案,既能简洁高效地保留元信息,又与标准库设计一致。其他选项或功能错位(C、D),或违背维护原则(B)。
发表于 今天 22:54:47 回复(0)