编程后端开发人员

解释一下 Python 中装饰器的工作原理。它们的主要用途是什么,如何实现,以及在使用过程中可能出现哪些细微差别?

用 Hintsage AI 助手通过面试

答案

装饰器 是接受另一个函数并返回一个具有扩展或改变行为的新函数的函数。它们通常用于日志记录、权限检查、缓存、执行时间等。

装饰器通过闭包(closure)或实现了 __call__ 方法的类来实现。经典语法如下:

# 简单装饰器 def simple_decorator(func): def wrapper(*args, **kwargs): print("在调用函数之前") result = func(*args, **kwargs) print("在调用函数之后") return result return wrapper @simple_decorator def my_func(): print("主函数") my_func()

执行结果如下:

在调用函数之前
主函数
在调用函数之后

主要用途 — 将重复的逻辑封装在主要业务逻辑之外。

细微差别:

  • 如果不使用 functools.wraps,原函数的名称和其文档字符串将丢失。
  • 如果装饰器接受参数,则需要写三层函数嵌套。

隐藏宝藏的问题

问题: "如果我用装饰器装饰一个函数,然后尝试通过 __name__ 获取函数名,我会看到什么,如何保留原始名称?"

答案:

默认情况下,名称将改为 wrapper 的名称(通常为 wrapper)。要保留原始元数据,请使用 functools.wraps

import functools def dec(f): @functools.wraps(f) def wrapper(*args): return f(*args) return wrapper @dec def foo(): pass print(foo.__name__) # 将打印 'foo',而不是 'wrapper'

由于对主题细微差别的不了解而导致的实际错误示例


故事

在大型资源自动化中,函数被装饰器包装后,使用反射测试函数名称的自动测试系统出现故障。问题在于缺少 functools.wraps


故事

添加日志记录的装饰器不支持具有不同签名的函数,因为未使用 *args, **kwargs。部分函数直接失败,静默失败。


故事

在一个包含 REST API 授权的项目中,开发者实现了一个带参数的装饰器,但忘记正确嵌套函数(是二级嵌套而不是三级)。结果是装饰器无法接受参数。