在 Python 中,装饰器(Decorator) 是一种特殊的函数或类,用于动态修改或增强其他函数或类的行为,而无需直接修改其源代码。装饰器本质上是一个高阶函数(即接收函数作为参数并返回函数),通过 @
语法糖(syntactic sugar)来简洁地应用。
装饰器的定义方式
装饰器的核心是一个可调用对象(函数或类),它接受一个函数作为输入,并返回一个新的函数(或类)。以下是定义装饰器的两种常见方式:
1. 函数装饰器(最常用)
装饰器本身是一个函数,它接收一个函数(被装饰的函数)作为参数,并返回一个新的函数(通常称为 wrapper
)。
基本结构
def decorator(func): # 1. 接收一个函数
def wrapper(*args, **kwargs): # 2. 定义包装函数
# 在这里添加额外功能(如日志、计时、权限检查等)
result = func(*args, **kwargs) # 3. 调用原函数
return result
return wrapper # 4. 返回包装后的函数
# 使用装饰器
@decorator
def target_function():
print("Hello, World!")
# 调用被装饰的函数
target_function() # 实际调用的是 wrapper()
示例:计算函数执行时间
import time
def timer(func):
def wrapper(*args, **kwargs):
start = time.time()
result = func(*args, **kwargs) # 执行原函数
end = time.time()
print(f"{func.__name__} 执行耗时: {end - start:.4f}秒")
return result
return wrapper
@timer
def slow_function(delay):
time.sleep(delay)
return "Done!"
slow_function(1) # 输出: slow_function 执行耗时: 1.0002秒
运行结果
2. 类装饰器
装饰器也可以是一个类,通过实现 __call__
方法使其可调用,从而装饰目标函数。
基本结构
class Decorator:
def __init__(self, func): # 初始化时接收被装饰的函数
self.func = func
def __call__(self, *args, **kwargs): # 实例可调用时执行
# 在这里添加额外功能
result = self.func(*args, **kwargs)
return result
# 使用装饰器
@Decorator
def target_function():
print("Hello, World!")
target_function() # 调用 __call__ 方法
示例:统计函数调用次数
class CountCalls:
def __init__(self, func):
self.func = func
self.calls = 0
def __call__(self, *args, **kwargs):
self.calls += 1
print(f"函数 {self.func.__name__} 被调用了 {self.calls} 次")
return self.func(*args, **kwargs)
@CountCalls
def greet(name):
print(f"Hello, {name}!")
greet("Alice") # 输出: 函数 greet 被调用了 1 次
greet("Bob") # 输出: 函数 greet 被调用了 2 次
运行结果:
3. 内置装饰器
Python 自带了一些常用的装饰器,通常用于特定的功能增强或元编程。
常见内置装饰器
装饰器 | 作用 |
---|---|
@property | 将方法变成属性调用(obj.x 而不是 obj.x() ) |
@classmethod | 定义类方法(第一个参数是 cls ,而非 self ) |
@staticmethod | 定义静态方法(无 self 或 cls 参数) |
@functools.lru_cache | 缓存函数结果,避免重复计算(常用于递归优化) |
示例
class Circle:
def __init__(self, radius):
self._radius = radius
@property # 将方法转为属性
def radius(self):
return self._radius
@classmethod # 类方法
def from_diameter(cls, diameter):
return cls(diameter / 2)
@staticmethod # 静态方法
def pi():
return 3.14159
# 使用
c = Circle.from_diameter(10) # 类方法调用
print(c.radius) # 输出: 5.0(通过属性访问)
print(Circle.pi()) # 输出: 3.14159(静态方法)
运行结果
装饰器的变种
1. 带参数的装饰器
如果装饰器本身需要参数,则需要三层嵌套函数:
def repeat(n): # 外层接收装饰器参数
def decorator(func): # 中层接收被装饰的函数
def wrapper(*args, **kwargs): # 内层包装逻辑
for _ in range(n):
func(*args, **kwargs)
return wrapper
return decorator
@repeat(3) # 调用 repeat(3) 返回 decorator,再装饰 say_hello
def say_hello():
print("Hello!")
say_hello() # 输出 3 次 "Hello!"
运行结果:
2. 保留原函数信息(functools.wraps
)
装饰器会覆盖原函数的 __name__
、__doc__
等元信息,可以使用 @functools.wraps(func)
修复:
from functools import wraps
def log_calls(func):
@wraps(func) # 保留原函数的元信息
def wrapper(*args, **kwargs):
print(f"调用 {func.__name__},参数: {args}, {kwargs}")
return func(*args, **kwargs)
return wrapper
@log_calls
def add(a, b):
"""计算两数之和"""
return a + b
print(add.__name__) # 输出 "add"(不加 wraps 会输出 "wrapper")
print(add.__doc__) # 输出 "计算两数之和"
运行结果:
总结
装饰器类型 | 定义方式 | 适用场景 |
---|---|---|
函数装饰器 | def decorator(func): ... | 通用功能扩展(日志、计时、权限) |
类装饰器 | class Decorator: def __call__ | 需要维护状态的装饰逻辑 |
带参数的装饰器 | def decorator(args): ... | 动态调整装饰行为 |
保留元信息的装饰器 | @functools.wraps(func) | 避免函数名、文档被覆盖 |
装饰器是 Python 元编程的重要工具,广泛应用于 Web 框架(Flask/Django)、测试、日志、缓存等场景。合理使用可以让代码更简洁、可维护性更高!