装饰器本质是"接收一个函数、返回一个新函数"的可调用对象,@decorator 只是 func = decorator(func) 的语法糖。利用闭包,新函数在原函数执行前后插入额外逻辑(日志、计时、鉴权、缓存等),而不修改原函数代码。

带参数的装饰器需要再包一层:外层接收参数,中层接收函数,内层是包装函数——即"装饰器工厂"。

import functools, time

def retry(times=3, delay=1):
    def decorator(func):
        @functools.wraps(func)  # 保留原函数元信息
        def wrapper(*args, **kwargs):
            for i in range(times):
                try:
                    return func(*args, **kwargs)
                except Exception:
                    if i == times - 1:
                        raise
                    time.sleep(delay)
        return wrapper
    return decorator

@retry(times=3, delay=0.5)
def fetch(): ...

关键点:@retry(...) 先执行 retry(3, 0.5) 得到真正的装饰器,再作用到 fetch 上,等价于 fetch = retry(3, 0.5)(fetch)。易错点:忘记 functools.wraps 会导致被装饰函数的 __name____doc__ 丢失,影响调试和框架自省。追问方向:类装饰器、装饰器执行顺序、如何用装饰器实现 Flask 路由。