
1. 原生装饰器(不带括号)
● 写法: @decorator
● Python 翻译: func = decorator(func)
● 执行逻辑:
● 一步到位。直接把下面的函数 func 扔进 decorator 里。
● 要求: decorator 必须直接接收一个函数作为参数。
2. 带参装饰器(带括号)
● 写法: @decorator(args)
● Python 翻译: func = decorator(args)(func)
● 执行逻辑:
● 分两步走(这也是你理解的精髓)。
● 第一步(计算结果): 先执行 decorator(args) 。这一步得到的结果(返回值)
● 第二步(真正装饰): 拿着第一步返回的那个“新装饰器”,去接收下面的函数 func 。
传1.3
当你写 @inner(1, 3) 时,Python 的执行逻辑发生了巨大的变化。它会先计算 @ 后面的表达式,也就是先运行 inner(1, 3) 。
● 它期望 inner 能处理这两个数字。
● 现实: 你的 inner 定义是 def inner(f): ,它只想要一个函数,不想要数字。
● 结果: 💥 Boom! 报错! (程序在这里就挂了,根本没机会进行第二步)。
传1
def inner(f): # 这里的 f 其实是数字 1
def wrapper(*args, **kwargs):
print("增强逻辑")
f() # 这里试图调用 1() -> 整数不可调用!
return wrapper
只要你在代码里使用了 @ 符号(语法糖),Python 解释器在底层执行时,必须、一定、永远会把下面紧挨着的那个函数对象作为第一个参数传进去。这是 Python 语言层面“写死”的规则,绝对固定,没有任何例外。
上面的错误
其实是因为混淆了 “装饰器工厂” 和 “装饰器”。
● 原生装饰器 ( @dec ): 直接干活的人。它只能接住原函数。
● 带参装饰器 ( @dec(1, 2) ): 这是一个 工厂。
● dec(1, 2) 这一步是在 制造 一个真正的装饰器。
● 制造出来的那个返回值(通常是一个闭包函数),才符合上面的“协议”,去接收原函数。