跳转至

带参数装饰器和普通装饰器有什么区别?

🔹 面试官问的这个问题挺常见的,我从三个层面来拆,其实本质就一句话:带参装饰器比普通装饰器多包了一层函数。


1️⃣ 定义方式:多了一层“壳”

  • 普通装饰器直接就是一个函数,接收 func,返回新函数(或可调用对象)。
# 普通装饰器:两层结构
def my_decorator(func):
    def wrapper(*args, **kwargs):
        print("before")
        result = func(*args, **kwargs)
        print("after")
        return result
    return wrapper
  • 👆 结构就是 decorator → wrapper

  • 带参数装饰器需要用“函数工厂”再包一层,因为要接收配置参数,然后返回真正的装饰器。

# 带参装饰器:三层结构
def repeat(times):
    def decorator(func):          # 这才是真正的装饰器
        def wrapper(*args, **kwargs):
            for _ in range(times):
                result = func(*args, **kwargs)
            return result
        return wrapper
    return decorator
  • 👆 结构变成 参数工厂 → decorator → wrapper,多了一层。

🧠 记忆点:普通装饰器是 @dec 直接拿函数;带参装饰器是 @dec(arg) 先调用工厂拿到装饰器,再用它去装饰函数。


2️⃣ 执行时机:一个在导入时,一个在调用时再多一步

  • 普通装饰器:@my_decorator 等价于 func = my_decorator(func),在模块加载时就执行装饰器函数,得到包装后的函数。

  • 带参装饰器:@repeat(3) 实际上是 func = repeat(3)(func),先执行 repeat(3) 返回 decorator,再立即把 func 传进去。 🕐 repeat(3) 这一步在定义函数时就执行了(跟普通装饰器外层一样),只不过它返回的是一个装饰器,紧接着执行内层装饰。

所以时间线上没有本质延迟,但多了一次函数调用,意味着你可以在 repeat(3) 里做参数校验、闭包捕获等额外工作。


3️⃣ 灵活性 & 应用场景

查看内嵌表格

✅ 比如 Flask 的 @app.route('/home') 就是典型的带参装饰器,路径是参数,框架先拿到路径,返回一个注册装饰器,再装饰视图函数。


🧩 一个直觉类比

  • 普通装饰器:像给手机贴一张固定的钢化膜,所有手机贴一样的膜。

  • 带参装饰器:像贴膜机,你可以设置参数(磨砂/防蓝光),机器再生产相应的膜,再去贴。

理解了这层“工厂返回装饰器”的机制,几乎所有带参装饰器的写法都能自己推出来。


我一般面试讲到这,对方就不会再追问了。顺便补一句,如果你能再深入说一下用 functools.wraps 保留元信息和用类实现装饰器,基本上这道题就拉满了。👌