装饰器在函数定义完成时立即执行,而非调用时;即@decorator在Python生成函数对象后立刻用其包装或替换原函数,发生在模块导入或脚本执行到该函数定义处的那一刻。
装饰器在函数被定义完成时立即执行,不是在函数被调用时。也就是说,@decorator 这一行代码的作用,是在 Python 解析完函数体、生成函数对象后,立刻用装饰器对这个函数对象进行包装或替换。
Python 解释器读到 def my_func(): ... 时,会:
- 先创建函数对象(my_func 是一个可调用对象)
- 然后检查它上面有没有装饰器
- 如果有(比如 @log_calls),就立刻把该函数对象作为参数传给 log_calls,并用返回值覆盖原名字 my_func
这个过程发生在模块导入(import)或脚本执行到该函数定义处的那一刻,和后续是否调用 my_func() 完全无关。
以常见写法为例:
def log_calls(func):
print(f"[装饰阶段] 正在包装函数 {func.__name__}")
def wrapper(*args, **kwargs):
print(f"[运行阶段] 调用 {func.__name__}")
return func(*args, **kwargs)
return wrapper
@log_calls
def greet(name):
return f"Hello, {name}!"
当你运行这段代码(比如直接执行或 import),你会立刻看到:
[装饰阶段] 正在包装函数 greet
而 [运行阶段] 那行输出,只有在你手动调用 greet("Alice") 时才会出现。
像 @retry(max_times=3) 这种带参数的装饰器,实际执行顺序是三步:
retry(max_times=3) → 返回一个真正的装饰器函数(比如叫 decorator)
decorator 去装饰目标函数(即 decorator(greet))greet
所以第一层函数(retry)在定义被装饰函数时就执行了;第二层(真正的装饰器)也在同一时刻执行;只有最内层的 wrapper 逻辑,留到函数被调用时才跑。
理解这点能避免常见陷阱:
print、日志、注册逻辑、配置读取等,都只发生一次(函数定义时),不是每次调用都触发config),它拿到的是定义时的值,不是运行时的最新值