Python代码不够高效!看装饰器如何改变游戏规则?
现代软件开发中,代码效率不仅关系到程序的运行速度,更影响着开发效率和代码的可维护性。Python作为一门优雅的编程语言,提供了许多强大的特性来提升代码质量,而装饰器(Decorator)就是其中最强大的特性之一。
1.1 什么是装饰器
装饰器是Python中的一个重要概念,它允许我们以非侵入式的方式修改或增强函数和类的行为。简单来说,装饰器就像是一个包装器,可以让我们在不改变原有代码的情况下,为其添加新的功能。
让我们看一个简单的例子:import time
def measure_time(func):
def wrapper():
start = time.time()
result = func()
print(f"函数执行时间: {time.time() - start}秒")
return result
return wrapper
@measure_time
def slow_function():
time.sleep(1)
return "完成"
# 使用装饰器
result = slow_function()
# 输出: 函数执行时间: 1.001秒
1.2 为什么需要装饰器
装饰器能帮助我们:
- 实现代码复用,避免重复编写相似的功能
- 提供更清晰的代码结构
- 实现横切关注点(如日志、性能监控等)
- 使代码更具可维护性和可读性
1.3 装饰器在Python生态中的应用
在很多流行的Python框架中,装饰器被广泛使用:# Flask中的路由装饰器
@app.route('/hello')
def hello():
return 'Hello, World!'
# Django中的视图装饰器
@login_required
def profile(request):
return render(request, 'profile.html')
# FastAPI中的路由装饰器
@app.get("/items/{item_id}")
async def read_item(item_id: int):
return {"item_id": item_id}
2. 装饰器基础
2.1 装饰器的语法和工作原理
装饰器的基本语法如下:from functools import wraps
def basic_decorator(func):
@wraps(func) # 保持原函数的元数据
def wrapper(*args, **kwargs):
# 在函数执行前的操作
print("函数执行前")
result = func(*args, **kwargs) # 执行原函数
# 在函数执行后的操作
print("函数执行后")
return result
return wrapper
# 使用装饰器
@basic_decorator
def greet(name):
return f"Hello, {name}!"
2.1.1 装饰器的执行流程
# 装饰器执行流程
"""
1. 定义装饰器
@decorator
def function():
pass
2. Python解释器执行过程
step 1: decorator = decorator_factory(args) # 如果是带参数的装饰器
step 2: function = decorator(function)
step 3: function(*args, **kwargs) # 实际调用时
"""当Python解释器遇到装饰器时,会按照以下步骤执行:
- 1. 如果是带参数的装饰器,首先执行装饰器工厂函数,获取实际的装饰器
- 2. 将被装饰的函数作为参数传递给装饰器
- 3. 用装饰器返回的新函数替换原始函数
- 4. 在实际调用时,执行包装后的函数
例如,对于带参数的装饰器:@decorator(arg1, arg2)
def function():
pass
# 等价于
function = decorator(arg1, arg2)(function)
2.2 装饰器的类型
2.2.1 函数装饰器
def timing_decorator(func):
@wraps(func)
def wrapper(*args, **kwargs):
start_time = time.time()
result = func(*args, **kwargs)
end_time = time.time()
print(f"函数 {func.__name__} 执行时间:{end_time - start_time}秒")
return result
return wrapper
@timing_decorator
def example_function():
time.sleep(1)
2.2.2 类装饰器
class TimingDecorator:
def __init__(self, func):
self.func = func
def __call__(self, *args, **kwargs):
start_time = time.time()
result = self.func(*args, **kwargs)
end_time = time.time()
print(f"函数 {self.func.__name__} 执行时间:{end_time - start_time}秒")
return result
def __set_name__(self, owner, name):
self.__name__ = name
@TimingDecorator
def example_class_decorator():
time.sleep(1)
2.3 装饰器链
装饰器可以叠加使用,从下往上依次执行:"""
装饰器链执行顺序示意图:
@decorator1
@decorator2
@decorator3
def function():
pass
执行顺序:
1. decorator3(function) -> wrapped_3
2. decorator2(wrapped_3) -> wrapped_2
3. decorator1(wrapped_2) -> wrapped_1
调用时:wrapped_1() -> wrapped_2() -> wrapped_3() -> function()
"""
def decorator1(func):
@wraps(func)
def wrapper(*args, **kwargs):
print("decorator1 开始")
result = func(*args, **kwargs)
print("decorator1 结束")
return result
return wrapper
def decorator2(func):
@wraps(func)
def wrapper(*args, **kwargs):
print("decorator2 开始")
result = func(*args, **kwargs)
print("decorator2 结束")
return result
return wrapper
@decorator1 # 后执行
@decorator2 # 先执行
def example():
print("函数执行")
# 执行结果:
# decorator1 开始
# decorator2 开始
# 函数执行
# decorator2 结束
# decorator1 结束
3. 装饰器的高级特性
3.1 带参数的装饰器
def repeat(times=3):
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=2)
def greet(name):
print(f"Hello, {name}!")
3.2 保持函数元数据
使用functools.wraps
保持原函数的元数据:from functools import wraps
def preserve_metadata(func):
@wraps(func) # 保持原函数的元数据
def wrapper(*args, **kwargs):
return func(*args, **kwargs)
return wrapper
@preserve_metadata
def example():
"""这是示例函数的文档字符串"""
pass
print(example.__name__) # 输出: example
print(example.__doc__) # 输出: 这是示例函数的文档字符串
4. 常见应用场景
4.1 统一的日志和错误处理
结合日志记录和错误处理的装饰器:import logging
from functools import wraps
def error_logging_handler(func):
"""
统一处理函数的日志记录和错误处理
- 记录函数的调用信息
- 记录函数的执行结果
- 捕获并记录异常信息
"""
@wraps(func)
def wrapper(*args, **kwargs):
try:
# 记录函数调用
logging.info(f"调用函数: {func.__name__}, 参数: args={args}, kwargs={kwargs}")
# 执行函数
result = func(*args, **kwargs)
# 记录成功信息
logging.info(f"函数 {func.__name__} 执行成功, 返回值: {result}")
return result
except Exception as e:
# 记录错误信息
logging.error(f"函数 {func.__name__} 执行失败: {str(e)}", exc_info=True)
raise
return wrapper
# 使用示例
@error_logging_handler
def divide(a, b):
return a / b
# 测试正常情况
result = divide(10, 2) # 正常执行,记录信息
# 测试异常情况
result = divide(10, 0) # 触发异常,记录错误
4.2 性能监控和分析
完整的性能监控装饰器:import time
import psutil
import functools
from typing import Optional
def measure_performance(threshold_ms: Optional[float] = None):
"""
性能监控装饰器
:param threshold_ms: 性能警告阈值(毫秒)
"""
def decorator(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
# 记录开始状态
start_time = time.perf_counter()
start_memory = psutil.Process().memory_info().rss
# 执行函数
result = func(*args, **kwargs)
# 计算性能指标
end_time = time.perf_counter()
end_memory = psutil.Process().memory_info().rss
execution_time = (end_time - start_time) * 1000 # 转换为毫秒
memory_used = (end_memory - start_memory) / 1024 / 1024 # 转换为MB
# 记录性能数据
performance_data = {
"function": func.__name__,
"execution_time_ms": execution_time,
"memory_used_mb": memory_used
}
# 检查是否超过阈值
if threshold_ms and execution_time > threshold_ms:
logging.warning(
f"性能警告: 函数 {func.__name__} 执行时间 {execution_time:.2f}ms "
f"超过阈值 {threshold_ms}ms"
)
# 输出性能报告
print(f"性能报告 - {func.__name__}:")
print(f"执行时间: {execution_time:.2f}ms")
print(f"内存使用: {memory_used:.2f}MB")
return result
return wrapper
return decorator
# 使用示例
@measure_performance(threshold_ms=100)
def process_data(data: list):
time.sleep(0.2) # 模拟耗时操作
return sum(data)
4.3 缓存机制
实现带过期时间的缓存装饰器:import time
from functools import wraps
from typing import Optional
from collections import OrderedDict
class TimedCache:
def __init__(self, max_size: int = 100, expire_seconds: Optional[float] = None):
"""
初始化缓存
:param max_size: 最大缓存条目数
:param expire_seconds: 缓存过期时间(秒)
"""
self.max_size = max_size
self.expire_seconds = expire_seconds
self.cache = OrderedDict()
def get(self, key):
if key not in self.cache:
return None
value, timestamp = self.cache[key]
# 检查是否过期
if self.expire_seconds and time.time() - timestamp > self.expire_seconds:
del self.cache[key]
return None
# 更新访问顺序
self.cache.move_to_end(key)
return value
def set(self, key, value):
# 检查容量
if len(self.cache) >= self.max_size:
self.cache.popitem(last=False) # 删除最早的项
# 存储值和时间戳
self.cache[key] = (value, time.time())
self.cache.move_to_end(key)
def cached(max_size: int = 100, expire_seconds: Optional[float] = None):
"""
缓存装饰器
:param max_size: 最大缓存条目数
:param expire_seconds: 缓存过期时间(秒)
"""
cache = TimedCache(max_size, expire_seconds)
def decorator(func):
@wraps(func)
def wrapper(*args, **kwargs):
# 生成缓存键
key = str((func.__name__, args, frozenset(kwargs.items())))
# 尝试获取缓存
cached_value = cache.get(key)
if cached_value is not None:
print(f"缓存命中: {func.__name__}")
return cached_value
# 计算新值
result = func(*args, **kwargs)
cache.set(key, result)
print(f"缓存更新: {func.__name__}")
return result
return wrapper
return decorator
# 使用示例
@cached(max_size=100, expire_seconds=60)
def fibonacci(n: int) -> int:
if n < 2:
return n
return fibonacci(n-1) + fibonacci(n-2)
4.4 访问控制和认证
实现基于角色的访问控制装饰器:from enum import Enum
from typing import List, Union
from functools import wraps
class Role(Enum):
GUEST = "guest"
USER = "user"
ADMIN = "admin"
class User:
def __init__(self, user_id: int, roles: List[Role]):
self.user_id = user_id
self.roles = roles
def require_roles(required_roles: Union[Role, List[Role]]):
"""
角色验证装饰器
:param required_roles: 需要的角色(单个角色或角色列表)
"""
if isinstance(required_roles, Role):
required_roles = [required_roles]
def decorator(func):
@wraps(func)
def wrapper(user: User, *args, **kwargs):
# 验证用户角色
if not any(role in user.roles for role in required_roles):
raise PermissionError(
f"需要角色 {[role.value for role in required_roles]} 中的任意一个"
)
return func(user, *args, **kwargs)
return wrapper
return decorator
# 使用示例
@require_roles([Role.ADMIN])
def delete_user(current_user: User, user_id: int):
print(f"管理员 {current_user.user_id} 删除了用户 {user_id}")
@require_roles([Role.USER, Role.ADMIN])
def edit_profile(current_user: User, user_id: int, data: dict):
print(f"用户 {current_user.user_id} 编辑了资料")
4.5 数据库连接管理
from contextlib import contextmanager
import pymysql
from typing import Generator
@contextmanager
def database_connection() -> Generator[pymysql.Connection, None, None]:
"""数据库连接上下文管理器"""
conn = pymysql.connect(
host='localhost',
user='user',
password='password',
database='db_name'
)
try:
yield conn
finally:
conn.close()
def with_db_connection(func):
"""数据库连接装饰器"""
@wraps(func)
def wrapper(*args, **kwargs):
with database_connection() as conn:
return func(conn,*args,**kwargs)
return wrapper
4.6 装饰器工厂
class DecoratorFactory:
"""装饰器工厂类"""
@staticmethod
def create_logging_decorator(log_level: str = 'INFO'):
def decorator(func):
@wraps(func)
def wrapper(*args, **kwargs):
logging.log(getattr(logging, log_level), f"调用函数: {func.__name__}")
return func(*args,**kwargs)
return wrapper
return decorator
@staticmethod
def create_timing_decorator(threshold_ms: float = None):
def decorator(func):
@wraps(func)
def wrapper(*args, **kwargs):
start = time.perf_counter()
result = func(*args, **kwargs)
duration = (time.perf_counter() - start) * 1000
if threshold_ms and duration > threshold_ms:
logging.warning(f"函数 {func.__name__} 执行时间超过阈值")
return result
return wrapper
return decorator
5. 实战案例
5.1 异步任务装饰器
import asyncio
from functools import wraps
from typing import Callable, Any
def async_task(func: Callable[..., Any]):
"""
将同步函数转换为异步任务的装饰器
"""
@wraps(func)
async def wrapper(*args, **kwargs):
# 在事件循环中运行同步函数
loop = asyncio.get_event_loop()
return await loop.run_in_executor(None, lambda: func(*args, **kwargs))
return wrapper
# 使用示例
@async_task
def cpu_intensive_task(n: int):
result = 0
for i in range(n):
result += i
return result
async def main():
# 异步执行CPU密集型任务
result = await cpu_intensive_task(1000000)
print(f"计算结果: {result}")
5.2 API响应格式化装饰器
from functools import wraps
from typing import Any, Dict
from datetime import datetime
def format_response(func):
"""
统一API响应格式的装饰器
"""
@wraps(func)
def wrapper(*args, **kwargs) -> Dict[str, Any]:
try:
result = func(*args, **kwargs)
return {
"success": True,
"data": result,
"timestamp": datetime.now().isoformat(),
"error": None
}
except Exception as e:
return {
"success": False,
"data": None,
"timestamp": datetime.now().isoformat(),
"error": {
"type": type(e).__name__,
"message": str(e)
}
}
return wrapper
# 使用示例
@format_response
def get_user_info(user_id: int):
if user_id < 0:
raise ValueError("用户ID不能为负数")
return {"id": user_id, "name": "测试用户"}
5.3 参数验证装饰器
from functools import wraps
from typing import Any, Callable, Dict, Type
def validate_params(**validators: Dict[str, Callable[[Any], bool]]):
"""
参数验证装饰器
:param validators: 参数名和对应的验证函数
"""
def decorator(func):
@wraps(func)
def wrapper(*args, **kwargs):
# 获取函数参数名
func_params = func.__code__.co_varnames[:func.__code__.co_argcount]
# 合并位置参数和关键字参数
all_args = dict(zip(func_params, args))
all_args.update(kwargs)
# 验证参数
for param_name, validator in validators.items():
if param_name in all_args:
value = all_args[param_name]
if not validator(value):
raise ValueError(f"参数 {param_name} 验证失败: {value}")
return func(*args, **kwargs)
return wrapper
return decorator
# 验证函数
def is_positive(x: int) -> bool:
return isinstance(x, int) and x > 0
def is_valid_name(name: str) -> bool:
return isinstance(name, str) and 2 <= len(name) <= 50
# 使用示例
@validate_params(
age=is_positive,
name=is_valid_name
)
def register_user(name: str, age: int):
return f"注册用户: {name}, 年龄: {age}"
6. 装饰器的高级技巧
6.1 类方法装饰器
from functools import wraps
from typing import Type, TypeVar
T = TypeVar('T')
def class_method_decorator(method):
"""
类方法装饰器,可以访问类和实例属性
"""
@wraps(method)
def wrapper(cls_or_self, *args, **kwargs):
if isinstance(cls_or_self, type):
# 处理类方法调用
print(f"类方法调用: {cls_or_self.__name__}")
else:
# 处理实例方法调用
print(f"实例方法调用: {cls_or_self.__class__.__name__}")
return method(cls_or_self, *args, **kwargs)
return wrapper
class MyClass:
@class_method_decorator
@classmethod
def class_method(cls):
return "类方法"
@class_method_decorator
def instance_method(self):
return "实例方法"
6.2 异步装饰器
import asyncio
from functools import wraps
from typing import Callable, Awaitable, TypeVar
T = TypeVar('T')
def async_retry(
max_attempts: int = 3,
delay: float = 1.0
) -> Callable[[Callable[..., Awaitable[T]]], Callable[..., Awaitable[T]]]:
"""
异步重试装饰器
:param max_attempts: 最大重试次数
:param delay: 重试间隔(秒)
"""
def decorator(func: Callable[..., Awaitable[T]]) -> Callable[..., Awaitable[T]]:
@wraps(func)
async def wrapper(*args, **kwargs) -> T:
last_exception = None
for attempt in range(max_attempts):
try:
return await func(*args, **kwargs)
except Exception as e:
last_exception = e
if attempt < max_attempts - 1:
await asyncio.sleep(delay)
else:
raise last_exception
raise last_exception # 类型检查器需要这行
return wrapper
return decorator
# 使用示例
@async_retry(max_attempts=3, delay=1.0)
async def fetch_data(url: str) -> dict:
# 模拟异步网络请求
await asyncio.sleep(0.1)
if random.random() < 0.5: # 模拟随机失败
raise ConnectionError("网络错误")
return {"data": "success"}
7. 性能考虑
7.1 装饰器对性能的影响
装饰器虽然能提供很多便利,但也会带来一定的性能开销:
- 1. 函数调用开销
# 每次函数调用都会增加一层包装
def simple_decorator(func):
@wraps(func)
def wrapper(*args, **kwargs):
return func(*args, **kwargs) # 额外的函数调用层级
return wrapper
- 1. 内存占用
# 每个装饰器都会创建新的闭包对象
def decorator_with_state(func):
state = {} # 这个字典会一直存在于内存中
@wraps(func)
def wrapper(*args, **kwargs):
return func(*args, **kwargs)
return wrapper
7.2 性能优化技巧
- 1. 使用
__slots__
优化内存使用:
class OptimizedDecorator:
__slots__ = ['func'] # 限制实例属性,减少内存使用
def __init__(self, func):
self.func = func
def __call__(self, *args, **kwargs):
return self.func(*args, **kwargs)
- 1. 使用缓存减少重复计算:
from functools import lru_cache
@lru_cache(maxsize=None)
def expensive_decorator(func):
def wrapper(*args, **kwargs):
return func(*args, **kwargs)
return wrapper
- 1. 避免不必要的装饰器嵌套:
# 不推荐
@decorator1
@decorator2
@decorator3
def function():
pass
# 推荐:合并装饰器功能
@combined_decorator
def function():
pass
7.3 性能测试示例
import time
import cProfile
def measure_decorator_overhead():
# 基准测试:无装饰器
def raw_function():
return sum(range(1000))
# 带装饰器的函数
@simple_decorator
def decorated_function():
return sum(range(1000))
# 性能测试
iterations = 100000
# 测试原始函数
start = time.perf_counter()
for _ in range(iterations):
raw_function()
raw_time = time.perf_counter() - start
# 测试装饰器函数
start = time.perf_counter()
for _ in range(iterations):
decorated_function()
decorated_time = time.perf_counter() - start
print(f"原始函数执行时间: {raw_time:.4f}秒")
print(f"装饰器函数执行时间: {decorated_time:.4f}秒")
print(f"装饰器开销: {((decorated_time - raw_time) / raw_time * 100):.2f}%")
8. 最佳实践
8.1 装饰器命名规范
# 好的命名示例
def validate_input(func):
pass
def cache_result(func):
pass
def require_authentication(func):
pass
# 避免的命名
def dec(func): # 太简短
pass
def my_decorator(func): # 不够具体
pass
8.2 文档化装饰器
def retry_on_failure(max_attempts=3, delay=1):
"""
在函数执行失败时自动重试的装饰器。
参数:
max_attempts (int): 最大重试次数
delay (int): 重试间隔时间(秒)
返回:
callable: 装饰后的函数
示例:
@retry_on_failure(max_attempts=3, delay=2)
def unstable_function():
pass
"""
def decorator(func):
@wraps(func)
def wrapper(*args, **kwargs):
for attempt in range(max_attempts):
try:
return func(*args, **kwargs)
except Exception as e:
if attempt == max_attempts - 1:
raise
time.sleep(delay)
return wrapper
return decorator
8.3 调试装饰器
def debug_decorator(func):
"""用于调试的装饰器,打印函数的调用信息"""
@wraps(func)
def wrapper(*args, **kwargs):
# 打印函数调用信息
print(f"\n[DEBUG] 正在调用: {func.__name__}")
print(f"[DEBUG] 参数: {args}, {kwargs}")
# 记录开始时间
start_time = time.perf_counter()
try:
# 执行函数
result = func(*args, **kwargs)
# 打印返回值
print(f"[DEBUG] 返回值: {result}")
return result
except Exception as e:
# 打印异常信息
print(f"[DEBUG] 异常: {type(e).__name__}: {str(e)}")
raise
finally:
# 打印执行时间
end_time = time.perf_counter()
print(f"[DEBUG] 执行时间: {end_time - start_time:.4f}秒")
return wrapper
8.4 常见陷阱和解决方案
- 1. 装饰器顺序问题:
# 装饰器的执行顺序是从下到上的
@decorator1 # 第二个执行
@decorator2 # 第一个执行
def function():
pass
# 解决方案:注意装饰器的依赖关系,合理安排顺序
- 1. 参数传递问题:
def decorator_with_args(arg1, arg2):
def decorator(func):
@wraps(func)
def wrapper(*args, **kwargs):
# 使用 arg1 和 arg2
return func(*args, **kwargs)
return wrapper
return decorator
9. 总结
9.1 装饰器的优势
9.1.1 代码复用
- 避免重复编写相似的功能
- 提高代码的可维护性
9.1.2 关注点分离
- 将横切关注点与业务逻辑分离
- 使代码结构更清晰
9.1.4 灵活性
- 非侵入式的代码修改
- 易于添加或移除功能
9.2 使用建议
9.2.1 合理使用
- 在确实需要的地方使用装饰器
- 避免过度使用
- 注意性能影响
9.2.2 开发建议
- 保持装饰器的单一职责
- 编写清晰的文档
- 做好错误处理
- 考虑向后兼容性
9.2.3 最佳实践
- 使用
functools.wraps
保持函数元数据 - 提供清晰的错误信息
- 考虑装饰器的可测试性
- 注意装饰器的执行顺序
- 定期检查性能影响
- 及时更新文档
来源: