引言:Python的魔法工具
在Python中,装饰器和上下文管理器是两个强大的高级特性,它们能让代码更加优雅、可读和高效。理解这两个特性,是成为Python高手的关键一步。
一、装饰器:函数的"装饰者"
1.1 装饰器基础:函数的函数
def simple_decorator(func):
"""最简单的装饰器"""
def wrapper():
print("函数执行前...")
func()
print("函数执行后...")
return wrapper
@simple_decorator
def say_hello():
print("Hello, Python!")
# 调用
say_hello()
# 输出:
# 函数执行前...
# Hello, Python!
# 函数执行后...
1.2 带参数的装饰器
def repeat(n_times):
"""带参数的装饰器工厂"""
def decorator(func):
def wrapper(*args, **kwargs):
for i in range(n_times):
print(f"第{i+1}次执行:")
result = func(*args, **kwargs)
return result
return wrapper
return decorator
@repeat(n_times=3)
def greet(name):
print(f"你好,{name}!")
greet("小明")
1.3 类装饰器:更强大的控制
class TimerDecorator:
"""计时装饰器类"""
def __init__(self, func):
self.func = func
import time
self.time = time
def __call__(self, *args, **kwargs):
start_time = self.time.time()
result = self.func(*args, **kwargs)
end_time = self.time.time()
print(f"函数 {self.func.__name__} 执行时间: {end_time - start_time:.6f}秒")
return result
@TimerDecorator
def heavy_computation():
import time
time.sleep(1)
return "计算完成"
result = heavy_computation()
1.4 实际应用:缓存装饰器
def cache_decorator(func):
"""缓存装饰器,避免重复计算"""
cache = {}
def wrapper(*args):
if args in cache:
print(f"从缓存中获取结果: {args}")
return cache[args]
result = func(*args)
cache[args] = result
print(f"计算并缓存结果: {args} -> {result}")
return result
return wrapper
@cache_decorator
def fibonacci(n):
"""计算斐波那契数列"""
if n <= 1:
return n
return fibonacci(n-1) + fibonacci(n-2)
# 测试
print(fibonacci(5)) # 只会计算必要的值
二、上下文管理器:优雅的资源管理
2.1 使用with语句
# 传统方式
file = open("example.txt", "w")
try:
file.write("Hello")
finally:
file.close()
# 使用上下文管理器
with open("example.txt", "w") as file:
file.write("Hello")
# 文件会自动关闭
2.2 创建自定义上下文管理器
# 基于类的上下文管理器
class DatabaseConnection:
def __init__(self, db_name):
self.db_name = db_name
self.connection = None
def __enter__(self):
print(f"连接到数据库: {self.db_name}")
# 模拟连接
self.connection = f"Connection to {self.db_name}"
return self.connection
def __exit__(self, exc_type, exc_val, exc_tb):
print(f"关闭数据库连接: {self.db_name}")
self.connection = None
if exc_type:
print(f"发生异常: {exc_type}")
# 返回False让异常继续传播
return False
# 使用
with DatabaseConnection("my_database") as conn:
print(f"使用连接: {conn}")
# 执行数据库操作
print("查询数据...")
2.3 使用contextlib模块
from contextlib import contextmanager
@contextmanager
def temp_chdir(path):
"""临时切换工作目录的上下文管理器"""
import os
original_path = os.getcwd()
try:
os.chdir(path)
print(f"切换到目录: {path}")
yield
finally:
os.chdir(original_path)
print(f"切换回目录: {original_path}")
# 使用
with temp_chdir("/tmp"):
import os
print(f"当前目录: {os.getcwd()}")
2.4 实际应用:事务管理
class Transaction:
"""数据库事务上下文管理器"""
def __init__(self, connection):
self.connection = connection
self.committed = False
def __enter__(self):
print("开始事务")
# 实际中会在这里开始数据库事务
return self
def __exit__(self, exc_type, exc_val, exc_tb):
if exc_type is None and not self.committed:
self.commit()
elif exc_type:
self.rollback()
print(f"事务回滚,原因: {exc_val}")
return False # 让异常继续传播
def commit(self):
print("提交事务")
self.committed = True
def rollback(self):
print("回滚事务")
# 使用示例
class MockConnection:
pass
try:
with Transaction(MockConnection()) as txn:
print("执行数据库操作1...")
# 模拟异常
raise ValueError("操作失败")
print("执行数据库操作2...") # 不会执行
except ValueError as e:
print(f"捕获到异常: {e}")
三、装饰器与上下文管理器的结合
3.1 创建测量性能的装饰器
from contextlib import contextmanager
import time
@contextmanager
def timer_context(name="操作"):
"""计时上下文管理器"""
start_time = time.time()
try:
yield
finally:
end_time = time.time()
print(f"{name}耗时: {end_time - start_time:.4f}秒")
def timer_decorator(func):
"""计时装饰器"""
def wrapper(*args, **kwargs):
with timer_context(func.__name__):
return func(*args, **kwargs)
return wrapper
@timer_decorator
def slow_function():
time.sleep(0.5)
return "完成"
slow_function()
3.2 实现重试机制
from functools import wraps
import time
def retry(max_attempts=3, delay=1):
"""重试装饰器"""
def decorator(func):
@wraps(func)
def wrapper(*args, **kwargs):
attempts = 0
while attempts < max_attempts:
try:
return func(*args, **kwargs)
except Exception as e:
attempts += 1
if attempts == max_attempts:
raise e
print(f"尝试 {attempts} 失败,{delay}秒后重试...")
time.sleep(delay)
return wrapper
return decorator
@retry(max_attempts=3, delay=2)
def unstable_api_call():
import random
if random.random() < 0.7: # 70%概率失败
raise ConnectionError("API调用失败")
return "成功"
# 测试
try:
result = unstable_api_call()
print(f"结果: {result}")
except Exception as e:
print(f"最终失败: {e}")
四、实际项目中的应用
4.1 Web框架中的装饰器
# Flask-like路由装饰器
class Router:
def __init__(self):
self.routes = {}
def route(self, path):
def decorator(func):
self.routes[path] = func
return func
return decorator
router = Router()
@router.route("/")
def home():
return "首页"
@router.route("/about")
def about():
return "关于我们"
# 模拟请求
def handle_request(path):
if path in router.routes:
return router.routes[path]()
return "404 Not Found"
print(handle_request("/")) # 首页
print(handle_request("/about")) # 关于我们
4.2 数据库连接池上下文管理器
class ConnectionPool:
"""数据库连接池"""
def __init__(self, size=5):
self.size = size
self.pool = []
self._create_pool()
def _create_pool(self):
for i in range(self.size):
self.pool.append(f"Connection-{i}")
@contextmanager
def get_connection(self):
"""获取连接的上下文管理器"""
if not self.pool:
raise RuntimeError("连接池已耗尽")
conn = self.pool.pop()
print(f"获取连接: {conn}")
try:
yield conn
finally:
print(f"释放连接: {conn}")
self.pool.append(conn)
def __enter__(self):
return self
def __exit__(self, *args):
print("关闭连接池")
self.pool.clear()
# 使用
pool = ConnectionPool(3)
with pool.get_connection() as conn1:
print(f"使用连接1: {conn1}")
with pool.get_connection() as conn2:
print(f"使用连接2: {conn2}")
# 可以嵌套使用
with pool.get_connection() as conn3:
print(f"使用连接3: {conn3}")
总结
装饰器和上下文管理器是Python中两个极其强大的特性:
-
装饰器:用于修改或增强函数的行为,实现关注点分离
- 日志记录
- 性能监测
- 权限验证
- 缓存实现
- 重试机制
-
上下文管理器:用于资源管理,确保资源正确释放
- 文件操作
- 数据库连接
- 锁机制
- 事务管理
- 临时配置