装饰器简介
什么是装饰器?
装饰器是一个可调用对象,它可以包装另一个函数或类,并且可以在不直接修改源代码的情况下扩展其功能。装饰器具有以下特点:
- 函数式编程:基于函数式编程的概念
- 代码复用:避免重复编写相似的代码
- 关注点分离:将通用功能与业务逻辑分开
- 可组合性:多个装饰器可以组合使用
装饰器的优势
- 代码简洁:减少样板代码
- 功能分离:提高代码可维护性
- 灵活扩展:不修改原函数即可添加功能
- 可重用性:装饰器可以应用于多个函数
基础装饰器
简单装饰器
def log_function(func):
def wrapper(*args, **kwargs):
print(f"调用函数: {func.__name__}")
result = func(*args, **kwargs)
print(f"函数返回: {result}")
return result
return wrapper
# 使用装饰器
@log_function
def add(a, b):
return a + b
# 调用函数
print(add(3, 5)) # 将打印调用信息并返回8
保留函数元信息
from functools import wraps
def log_function(func):
@wraps(func) # 保留原函数的元信息
def wrapper(*args, **kwargs):
print(f"调用函数: {func.__name__}")
result = func(*args, **kwargs)
print(f"函数返回: {result}")
return result
return wrapper
@log_function
def greet(name):
"""打招呼函数"""
return f"Hello, {name}!"
# 现在可以正确访问函数的文档字符串和名称
print(greet.__doc__) # 输出: 打招呼函数
print(greet.__name__) # 输出: greet
带参数装饰器
装饰器工厂
def repeat(times):
def decorator(func):
@wraps(func)
def wrapper(*args, **kwargs):
for _ in range(times):
result = func(*args, **kwargs)
return result
return wrapper
return decorator
# 使用带参数的装饰器
@repeat(times=3)
def greet(name):
print(f"Hello, {name}!")
return "Done"
# 调用函数将打印三次问候
greet("Alice")
多层装饰器
def bold(func):
@wraps(func)
def wrapper():
return f"{func()}"
return wrapper
def italic(func):
@wraps(func)
def wrapper():
return f"{func()}"
return wrapper
@bold
@italic
def hello():
return "Hello, World!"
print(hello()) # 输出: Hello, World!
类装饰器
装饰类的装饰器
def singleton(cls):
"""实现单例模式的装饰器"""
instances = {}
@wraps(cls)
def get_instance(*args, **kwargs):
if cls not in instances:
instances[cls] = cls(*args, **kwargs)
return instances[cls]
return get_instance
@singleton
class Database:
def __init__(self):
print("初始化数据库连接...")
# 测试单例模式
db1 = Database()
db2 = Database()
print(db1 is db2) # True,说明是同一个实例
类作为装饰器
class Timer:
def __init__(self, prefix=""):
self.prefix = prefix
def __call__(self, func):
@wraps(func)
def wrapper(*args, **kwargs):
import time
start = time.time()
result = func(*args, **kwargs)
end = time.time()
print(f"{self.prefix}函数执行时间: {end - start:.2f}秒")
return result
return wrapper
@Timer(prefix="[DEBUG]")
def slow_function():
import time
time.sleep(1)
return "完成"
slow_function() # 输出执行时间
实际应用
缓存装饰器
def memoize(func):
"""实现函数结果缓存的装饰器"""
cache = {}
@wraps(func)
def wrapper(*args):
if args not in cache:
cache[args] = func(*args)
return cache[args]
return wrapper
@memoize
def fibonacci(n):
if n < 2:
return n
return fibonacci(n-1) + fibonacci(n-2)
# 测试缓存效果
print(fibonacci(100)) # 第二次调用会非常快
权限验证装饰器
def require_auth(role):
"""检查用户角色的装饰器"""
def decorator(func):
@wraps(func)
def wrapper(*args, **kwargs):
# 模拟获取当前用户角色
current_role = get_current_user_role()
if current_role != role:
raise PermissionError(
f"需要 {role} 权限,但当前用户是 {current_role}"
)
return func(*args, **kwargs)
return wrapper
return decorator
@require_auth(role="admin")
def delete_user(user_id):
print(f"删除用户: {user_id}")
# 使用示例
try:
delete_user(123)
except PermissionError as e:
print(e)
最佳实践
设计建议
- 保持简单:装饰器应该只做一件事
- 使用functools.wraps:保留原函数的元信息
- 考虑副作用:避免修改函数的预期行为
- 异常处理:妥善处理装饰器中的异常
错误处理示例
def safe_division(func):
"""处理除零异常的装饰器"""
@wraps(func)
def wrapper(*args, **kwargs):
try:
return func(*args, **kwargs)
except ZeroDivisionError:
print("错误:除数不能为零")
return None
return wrapper
@safe_division
def divide(a, b):
return a / b
# 测试异常处理
print(divide(10, 2)) # 正常输出:5.0
print(divide(10, 0)) # 输出错误信息并返回None