装饰器是Python进阶的标志性知识点,也是面试中区分初级和中高级Python开发者的关键题目。从Flask的@app.route到Django的@login_required,装饰器无处不在。字节、阿里等大厂Python岗面试必考装饰器的原理和手写能力。
从基础概念讲起:Python中函数是一等公民,可以作为参数传递、作为返回值、赋值给变量。
解释闭包:内部函数引用了外部函数的变量,且外部函数已经返回,内部函数仍能访问这些变量。
定义装饰器:接收一个函数作为参数,返回一个新函数的高阶函数。@decorator语法是func = decorator(func)的语法糖。
讲标准实现模式:外层函数接收被装饰函数,内层wrapper函数通过*args/**kwargs转发参数,在调用原函数前后添加逻辑。
讲进阶用法:带参数的装饰器(三层嵌套)、类装饰器(__call__方法)、functools.wraps保留元信息、多个装饰器堆叠的执行顺序。
Python装饰器的原理基于两个核心概念:函数是一等对象和闭包。在Python中,函数可以像普通变量一样传递和返回。闭包是指内部函数引用了外部作用域的变量,即使外部函数执行结束,这些变量仍然存活在内部函数的__closure__中。装饰器本质上就是一个接收函数、返回函数的高阶函数。@my_decorator放在函数定义上方,等价于func = my_decorator(func)。标准写法是:def my_decorator(func): @functools.wraps(func) def wrapper(*args, **kwargs): 前置逻辑; result = func(*args, **kwargs); 后置逻辑; return result; return wrapper。这里functools.wraps非常重要,它会把原函数的__name__、__doc__、__module__等元信息复制到wrapper上,不加的话调试时看到的函数名是wrapper而不是原函数名。带参数的装饰器需要三层嵌套:最外层接收装饰器参数,中间层接收被装饰函数,内层是wrapper。例如@retry(max_attempts=3),先执行retry(max_attempts=3)返回真正的装饰器,再用这个装饰器包装函数。类装饰器通过实现__init__(接收函数)和__call__(执行逻辑)来实现,好处是可以维护调用间的状态。多个装饰器堆叠时从下往上包装:@A @B def func相当于func = A(B(func)),执行时A的前置逻辑先执行。实际项目中常见的装饰器包括:Flask的@app.route绑定路由、@property实现属性访问器、@functools.lru_cache实现缓存、@pytest.fixture管理测试依赖等。
面试时很可能要求你手写一个装饰器——提前练习计时器、重试器、权限校验等常见场景。
一定要提到functools.wraps,这是区分是否有实战经验的关键细节。
理解装饰器堆叠的执行顺序,这是常见追问。
面试中如果忘了三层嵌套的写法,即答侠可以实时提示完整的代码结构。
@decorator直接把函数传给decorator(func)。@decorator()先调用decorator()获取返回值(真正的装饰器),再用这个装饰器包装函数。加括号的版本用于传递参数给装饰器。
可以。类装饰器接收一个类并返回修改后的类。@dataclass就是典型的类装饰器,它会自动生成__init__、__repr__等方法。
不一样。Python装饰器会实际修改函数行为(运行时包装),Java注解只是元数据标记,需要配合反射或框架来实现额外行为。Python装饰器更强大也更灵活。