Python中的函数装饰器执行时机与装饰器参数处理机制
字数 1233 2025-12-11 23:04:33
Python中的函数装饰器执行时机与装饰器参数处理机制
描述:
函数装饰器是Python中一种强大的语法糖,用于修改或增强函数的行为。理解装饰器的执行时机(何时执行装饰器代码)和参数处理机制(如何处理带参数的装饰器)是掌握装饰器的关键。本文将详细讲解装饰器的执行顺序、参数传递以及常见应用场景。
解题过程循序渐进讲解:
步骤1:基础装饰器的执行时机
装饰器的基本语法是在函数定义前使用@decorator。当Python解释器遇到装饰器时,它会立即执行装饰器函数,而不是在被装饰函数调用时执行。
def my_decorator(func):
print("装饰器执行了!")
return func
@my_decorator
def my_function():
print("函数执行了!")
# 输出:装饰器执行了!(在导入模块时立即输出)
# 此时my_function已经是my_decorator返回的原始函数
关键点:
- 装饰器在模块导入时(函数定义时)立即执行
- 装饰器接收被装饰函数作为参数
- 装饰器返回的函数会替换原始函数
步骤2:装饰器返回包装函数
通常装饰器返回一个包装函数(wrapper),在包装函数内部调用原始函数,并添加额外功能。
def my_decorator(func):
def wrapper():
print("调用前执行的操作")
func()
print("调用后执行的操作")
return wrapper
@my_decorator
def greet():
print("Hello, World!")
greet()
# 输出:
# 调用前执行的操作
# Hello, World!
# 调用后执行的操作
此时执行时机:
- 模块导入时:
my_decorator(greet)执行,返回wrapper函数 - 调用
greet()时:实际调用的是wrapper()函数
步骤3:处理被装饰函数的参数
为了使装饰器能处理任意参数的函数,需要使用*args和**kwargs。
def my_decorator(func):
def wrapper(*args, **kwargs):
print(f"函数 {func.__name__} 被调用,参数: {args}, {kwargs}")
result = func(*args, **kwargs)
print(f"函数返回: {result}")
return result
return wrapper
@my_decorator
def add(a, b):
return a + b
add(3, 5) # 输出函数调用和返回信息
步骤4:带参数的装饰器
有时需要装饰器本身接受参数。这需要创建一个返回装饰器的函数(装饰器工厂)。
def repeat(times):
"""重复执行函数指定次数的装饰器"""
def decorator(func):
def wrapper(*args, **kwargs):
results = []
for i in range(times):
print(f"第 {i+1} 次执行")
result = func(*args, **kwargs)
results.append(result)
return results
return wrapper
return decorator
@repeat(times=3)
def say_hello(name):
print(f"Hello, {name}!")
return f"Greeted {name}"
say_hello("Alice")
执行时机分析:
@repeat(times=3)首先执行repeat(3),返回decorator函数- 然后执行
decorator(say_hello),返回wrapper函数 - 最终
say_hello被替换为wrapper函数
步骤5:多层装饰器的执行顺序
当多个装饰器堆叠时,执行顺序是从下往上(从内往外)。
def decorator1(func):
def wrapper():
print("装饰器1 - 前")
func()
print("装饰器1 - 后")
return wrapper
def decorator2(func):
def wrapper():
print("装饰器2 - 前")
func()
print("装饰器2 - 后")
return wrapper
@decorator1
@decorator2
def my_func():
print("原始函数")
my_func()
# 输出:
# 装饰器1 - 前
# 装饰器2 - 前
# 原始函数
# 装饰器2 - 后
# 装饰器1 - 后
执行顺序等价于:decorator1(decorator2(my_func))
步骤6:使用functools.wraps保留元数据
装饰器会掩盖原始函数的元数据(如函数名、文档字符串等)。使用functools.wraps可以解决这个问题。
import functools
def my_decorator(func):
@functools.wraps(func) # 保留原始函数的元数据
def wrapper(*args, **kwargs):
"""包装函数的文档字符串"""
return func(*args, **kwargs)
return wrapper
@my_decorator
def example():
"""原始函数的文档字符串"""
pass
print(example.__name__) # 输出: example
print(example.__doc__) # 输出: 原始函数的文档字符串
步骤7:类装饰器
装饰器也可以使用类实现,通过实现__call__方法。
class CountCalls:
def __init__(self, func):
self.func = func
self.call_count = 0
functools.update_wrapper(self, func) # 更新包装器属性
def __call__(self, *args, **kwargs):
self.call_count += 1
print(f"函数 {self.func.__name__} 第 {self.call_count} 次调用")
return self.func(*args, **kwargs)
@CountCalls
def hello():
print("Hello!")
hello() # 输出调用次数
hello() # 调用次数递增
步骤8:带参数的类装饰器
类装饰器也可以接受参数,通过实现__init__方法来接收装饰器参数。
class Delay:
def __init__(self, seconds):
self.seconds = seconds
def __call__(self, func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
import time
time.sleep(self.seconds)
return func(*args, **kwargs)
return wrapper
@Delay(seconds=2)
def slow_function():
print("函数执行完成")
# 调用时会延迟2秒
关键要点总结:
- 执行时机:装饰器在模块导入/函数定义时立即执行,而不是在函数调用时
- 参数传递:
- 基础装饰器:接收被装饰函数作为参数
- 带参数装饰器:先接收装饰器参数,返回真正的装饰器,再接收被装饰函数
- 执行顺序:多个装饰器时,从下往上执行(从最靠近函数的装饰器开始)
- 元数据保留:使用
functools.wraps保持原始函数的属性 - 实现方式:可以使用函数或类(实现
__call__方法)实现装饰器 - 返回结果:装饰器必须返回一个可调用对象,通常返回包装函数
理解这些机制有助于编写更灵活、可重用的装饰器,并在调试时理解装饰器的执行流程。