Python高级特性:深入理解装饰器与上下文管理器

25 阅读5分钟

引言: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中两个极其强大的特性:

  1. 装饰器:用于修改或增强函数的行为,实现关注点分离

    • 日志记录
    • 性能监测
    • 权限验证
    • 缓存实现
    • 重试机制
  2. 上下文管理器:用于资源管理,确保资源正确释放

    • 文件操作
    • 数据库连接
    • 锁机制
    • 事务管理
    • 临时配置