023-上下文管理器

023-上下文管理器

🔴 难度: 高级 | ⏱️ 预计时间: 4小时 | 📋 前置: 022-生成器与迭代器

学习目标

完成本章节后,你将能够:

  • 理解上下文管理器的概念和作用
  • 掌握with语句的使用方法
  • 实现自定义上下文管理器
  • 使用contextlib模块简化上下文管理器的创建
  • 应用上下文管理器解决资源管理问题
  • 设计复杂的上下文管理器系统

上下文管理器基础

什么是上下文管理器

上下文管理器是一个定义了在with语句中使用的运行时上下文的对象。它负责执行进入和退出所需的操作,确保资源的正确获取和释放。

# 上下文管理器基础概念
print("=== 上下文管理器基础概念 ===")

# 最常见的上下文管理器:文件操作
print("\n1. 文件操作上下文管理器:")

# 传统方式(容易出错)
print("传统方式:")
try:
    file = open('example.txt', 'w')
    file.write('Hello, World!')
finally:
    file.close()  # 必须手动关闭

# 使用with语句(推荐)
print("\nwith语句方式:")
with open('example.txt', 'w') as file:
    file.write('Hello, World!')  # 自动关闭文件

print("文件已自动关闭")

# 验证文件是否关闭
print(f"文件是否关闭: {file.closed}")

# 多个上下文管理器
print("\n2. 多个上下文管理器:")
with open('input.txt', 'w') as input_file, open('output.txt', 'w') as output_file:
    input_file.write('输入数据')
    output_file.write('输出数据')
    print("两个文件都会自动关闭")

# 嵌套上下文管理器
print("\n3. 嵌套上下文管理器:")
with open('outer.txt', 'w') as outer_file:
    outer_file.write('外层文件\n')
    with open('inner.txt', 'w') as inner_file:
        inner_file.write('内层文件\n')
        print("嵌套文件操作")
    print("内层文件已关闭")
print("外层文件已关闭")

上下文管理器协议

上下文管理器协议定义了两个特殊方法:__enter____exit__

# 上下文管理器协议
print("\n=== 上下文管理器协议 ===")

class SimpleContextManager:
    """简单的上下文管理器示例"""
    
    def __init__(self, name):
        self.name = name
        print(f"初始化上下文管理器: {self.name}")
    
    def __enter__(self):
        """进入上下文时调用"""
        print(f"进入上下文: {self.name}")
        return self  # 返回值会赋给as后的变量
    
    def __exit__(self, exc_type, exc_value, traceback):
        """退出上下文时调用"""
        print(f"退出上下文: {self.name}")
        if exc_type is not None:
            print(f"异常类型: {exc_type.__name__}")
            print(f"异常值: {exc_value}")
            print(f"异常追踪: {traceback}")
            return False  # 不抑制异常
        return True
    
    def do_something(self):
        print(f"在上下文中执行操作: {self.name}")

# 使用自定义上下文管理器
print("\n1. 正常使用:")
with SimpleContextManager("测试管理器") as manager:
    manager.do_something()

print("\n2. 异常情况:")
try:
    with SimpleContextManager("异常测试") as manager:
        manager.do_something()
        raise ValueError("测试异常")
except ValueError as e:
    print(f"捕获异常: {e}")

print("\n3. 多个上下文管理器:")
with SimpleContextManager("第一个") as first, SimpleContextManager("第二个") as second:
    first.do_something()
    second.do_something()

上下文管理器的执行流程

# 上下文管理器执行流程详解
print("\n=== 上下文管理器执行流程 ===")

class DetailedContextManager:
    """详细展示执行流程的上下文管理器"""
    
    def __init__(self, name):
        self.name = name
        print(f"步骤1: 创建对象 {self.name}")
    
    def __enter__(self):
        print(f"步骤2: 调用 __enter__ 方法 ({self.name})")
        print(f"步骤3: 获取资源 ({self.name})")
        return f"资源_{self.name}"
    
    def __exit__(self, exc_type, exc_value, traceback):
        print(f"步骤6: 调用 __exit__ 方法 ({self.name})")
        print(f"步骤7: 释放资源 ({self.name})")
        
        if exc_type:
            print(f"步骤8: 处理异常 {exc_type.__name__}: {exc_value}")
            # 返回True表示抑制异常,False表示不抑制
            return False
        
        print(f"步骤8: 正常退出 ({self.name})")
        return True

print("\n执行流程演示:")
with DetailedContextManager("流程演示") as resource:
    print(f"步骤4: 进入with代码块,resource = {resource}")
    print(f"步骤5: 执行with代码块中的代码")
    # 这里是with代码块的内容
    pass

print("\n异常处理流程:")
try:
    with DetailedContextManager("异常流程") as resource:
        print(f"步骤4: 进入with代码块,resource = {resource}")
        print(f"步骤5: 执行代码并抛出异常")
        raise RuntimeError("模拟异常")
except RuntimeError as e:
    print(f"步骤9: 在外部捕获异常: {e}")

实用的上下文管理器

资源管理上下文管理器

# 实用的资源管理上下文管理器
print("\n=== 资源管理上下文管理器 ===")

import time
import threading
from typing import Optional, Any

class TimerContext:
    """计时上下文管理器"""
    
    def __init__(self, name: str = "操作"):
        self.name = name
        self.start_time: Optional[float] = None
        self.end_time: Optional[float] = None
    
    def __enter__(self):
        print(f"开始计时: {self.name}")
        self.start_time = time.time()
        return self
    
    def __exit__(self, exc_type, exc_value, traceback):
        self.end_time = time.time()
        duration = self.end_time - self.start_time
        print(f"结束计时: {self.name}, 耗时: {duration:.4f}秒")
        return False
    
    @property
    def duration(self) -> float:
        """获取执行时间"""
        if self.start_time and self.end_time:
            return self.end_time - self.start_time
        return 0.0

class LockContext:
    """锁管理上下文管理器"""
    
    def __init__(self, lock: threading.Lock, timeout: float = 5.0):
        self.lock = lock
        self.timeout = timeout
        self.acquired = False
    
    def __enter__(self):
        print(f"尝试获取锁,超时时间: {self.timeout}秒")
        self.acquired = self.lock.acquire(timeout=self.timeout)
        if not self.acquired:
            raise TimeoutError(f"无法在{self.timeout}秒内获取锁")
        print("成功获取锁")
        return self
    
    def __exit__(self, exc_type, exc_value, traceback):
        if self.acquired:
            self.lock.release()
            print("释放锁")
        return False

class DatabaseConnection:
    """模拟数据库连接上下文管理器"""
    
    def __init__(self, connection_string: str):
        self.connection_string = connection_string
        self.connection = None
        self.transaction_active = False
    
    def __enter__(self):
        print(f"连接数据库: {self.connection_string}")
        # 模拟连接过程
        self.connection = f"Connection_{id(self)}"
        print(f"数据库连接成功: {self.connection}")
        return self
    
    def __exit__(self, exc_type, exc_value, traceback):
        if self.transaction_active:
            if exc_type:
                print("检测到异常,回滚事务")
                self.rollback()
            else:
                print("正常退出,提交事务")
                self.commit()
        
        print(f"关闭数据库连接: {self.connection}")
        self.connection = None
        return False
    
    def begin_transaction(self):
        """开始事务"""
        print("开始事务")
        self.transaction_active = True
    
    def commit(self):
        """提交事务"""
        print("提交事务")
        self.transaction_active = False
    
    def rollback(self):
        """回滚事务"""
        print("回滚事务")
        self.transaction_active = False
    
    def execute(self, sql: str):
        """执行SQL"""
        print(f"执行SQL: {sql}")
        return f"Result of {sql}"

# 演示资源管理上下文管理器
print("\n1. 计时上下文管理器:")
with TimerContext("数据处理") as timer:
    # 模拟一些耗时操作
    time.sleep(0.1)
    print("正在处理数据...")
    time.sleep(0.1)

print(f"总耗时: {timer.duration:.4f}秒")

print("\n2. 锁管理上下文管理器:")
lock = threading.Lock()
with LockContext(lock, timeout=2.0):
    print("在锁保护下执行关键代码")
    time.sleep(0.1)

print("\n3. 数据库连接上下文管理器:")
# 正常情况
with DatabaseConnection("postgresql://localhost:5432/mydb") as db:
    db.begin_transaction()
    db.execute("INSERT INTO users (name) VALUES ('Alice')")
    db.execute("UPDATE users SET age = 25 WHERE name = 'Alice'")

print("\n异常情况:")
try:
    with DatabaseConnection("postgresql://localhost:5432/mydb") as db:
        db.begin_transaction()
        db.execute("INSERT INTO users (name) VALUES ('Bob')")
        raise ValueError("模拟数据库操作异常")
except ValueError as e:
    print(f"捕获异常: {e}")

状态管理上下文管理器

# 状态管理上下文管理器
print("\n=== 状态管理上下文管理器 ===")

import os
import sys
from typing import Dict, Any

class EnvironmentContext:
    """环境变量管理上下文管理器"""
    
    def __init__(self, **env_vars):
        self.env_vars = env_vars
        self.original_values = {}
    
    def __enter__(self):
        print(f"设置环境变量: {self.env_vars}")
        # 保存原始值
        for key in self.env_vars:
            self.original_values[key] = os.environ.get(key)
        
        # 设置新值
        for key, value in self.env_vars.items():
            os.environ[key] = str(value)
        
        return self
    
    def __exit__(self, exc_type, exc_value, traceback):
        print("恢复环境变量")
        # 恢复原始值
        for key, original_value in self.original_values.items():
            if original_value is None:
                os.environ.pop(key, None)
            else:
                os.environ[key] = original_value
        return False

class WorkingDirectoryContext:
    """工作目录管理上下文管理器"""
    
    def __init__(self, new_dir: str):
        self.new_dir = new_dir
        self.original_dir = None
    
    def __enter__(self):
        self.original_dir = os.getcwd()
        print(f"切换工作目录: {self.original_dir} -> {self.new_dir}")
        
        # 创建目录(如果不存在)
        os.makedirs(self.new_dir, exist_ok=True)
        os.chdir(self.new_dir)
        
        return self
    
    def __exit__(self, exc_type, exc_value, traceback):
        print(f"恢复工作目录: {self.new_dir} -> {self.original_dir}")
        os.chdir(self.original_dir)
        return False

class AttributeContext:
    """对象属性临时修改上下文管理器"""
    
    def __init__(self, obj: Any, **attrs):
        self.obj = obj
        self.new_attrs = attrs
        self.original_attrs = {}
    
    def __enter__(self):
        print(f"临时修改对象属性: {self.new_attrs}")
        # 保存原始属性值
        for attr_name in self.new_attrs:
            if hasattr(self.obj, attr_name):
                self.original_attrs[attr_name] = getattr(self.obj, attr_name)
            else:
                self.original_attrs[attr_name] = AttributeError(f"属性 {attr_name} 不存在")
        
        # 设置新属性值
        for attr_name, attr_value in self.new_attrs.items():
            setattr(self.obj, attr_name, attr_value)
        
        return self.obj
    
    def __exit__(self, exc_type, exc_value, traceback):
        print("恢复对象属性")
        # 恢复原始属性值
        for attr_name, original_value in self.original_attrs.items():
            if isinstance(original_value, AttributeError):
                delattr(self.obj, attr_name)
            else:
                setattr(self.obj, attr_name, original_value)
        return False

class ConfigContext:
    """配置管理上下文管理器"""
    
    def __init__(self, config_dict: Dict[str, Any], **config_updates):
        self.config = config_dict
        self.updates = config_updates
        self.backup = {}
    
    def __enter__(self):
        print(f"临时更新配置: {self.updates}")
        # 备份原始配置
        for key in self.updates:
            if key in self.config:
                self.backup[key] = self.config[key]
            else:
                self.backup[key] = KeyError(f"配置项 {key} 不存在")
        
        # 应用更新
        self.config.update(self.updates)
        return self.config
    
    def __exit__(self, exc_type, exc_value, traceback):
        print("恢复原始配置")
        # 恢复配置
        for key, original_value in self.backup.items():
            if isinstance(original_value, KeyError):
                self.config.pop(key, None)
            else:
                self.config[key] = original_value
        return False

# 演示状态管理上下文管理器
print("\n1. 环境变量管理:")
print(f"原始PATH长度: {len(os.environ.get('PATH', ''))}")
with EnvironmentContext(TEST_VAR="测试值", PATH="/custom/path"):
    print(f"临时TEST_VAR: {os.environ.get('TEST_VAR')}")
    print(f"临时PATH: {os.environ.get('PATH')}")
print(f"恢复后PATH长度: {len(os.environ.get('PATH', ''))}")
print(f"恢复后TEST_VAR: {os.environ.get('TEST_VAR', '不存在')}")

print("\n2. 工作目录管理:")
print(f"当前目录: {os.getcwd()}")
with WorkingDirectoryContext("/tmp/test_context"):
    print(f"临时目录: {os.getcwd()}")
    # 在临时目录中创建文件
    with open("temp_file.txt", "w") as f:
        f.write("临时文件内容")
print(f"恢复目录: {os.getcwd()}")

print("\n3. 对象属性管理:")
class TestObject:
    def __init__(self):
        self.value = 100
        self.name = "原始名称"

test_obj = TestObject()
print(f"原始属性: value={test_obj.value}, name={test_obj.name}")

with AttributeContext(test_obj, value=200, name="临时名称", new_attr="新属性") as obj:
    print(f"临时属性: value={obj.value}, name={obj.name}, new_attr={obj.new_attr}")

print(f"恢复属性: value={test_obj.value}, name={test_obj.name}")
print(f"new_attr存在: {hasattr(test_obj, 'new_attr')}")

print("\n4. 配置管理:")
app_config = {
    "debug": False,
    "database_url": "postgresql://prod",
    "cache_size": 1000
}

print(f"原始配置: {app_config}")
with ConfigContext(app_config, debug=True, database_url="sqlite://test", new_setting="测试") as config:
    print(f"临时配置: {config}")
print(f"恢复配置: {app_config}")

contextlib模块

使用contextlib简化上下文管理器

# 使用contextlib模块
print("\n=== contextlib模块 ===")

import contextlib
from contextlib import contextmanager, ExitStack, suppress, redirect_stdout, redirect_stderr
from io import StringIO
import tempfile
import shutil

# 1. @contextmanager装饰器
print("\n1. @contextmanager装饰器:")

@contextmanager
def simple_timer(name):
    """使用生成器创建上下文管理器"""
    import time
    print(f"开始计时: {name}")
    start_time = time.time()
    try:
        yield start_time  # yield前的代码相当于__enter__
    finally:
        # yield后的代码相当于__exit__
        end_time = time.time()
        print(f"结束计时: {name}, 耗时: {end_time - start_time:.4f}秒")

@contextmanager
def temporary_attribute(obj, attr_name, temp_value):
    """临时修改对象属性"""
    original_value = getattr(obj, attr_name, None)
    has_attr = hasattr(obj, attr_name)
    
    print(f"设置临时属性 {attr_name} = {temp_value}")
    setattr(obj, attr_name, temp_value)
    
    try:
        yield obj
    finally:
        if has_attr:
            setattr(obj, attr_name, original_value)
            print(f"恢复属性 {attr_name} = {original_value}")
        else:
            delattr(obj, attr_name)
            print(f"删除临时属性 {attr_name}")

@contextmanager
def database_transaction():
    """数据库事务上下文管理器"""
    print("开始事务")
    transaction_id = f"txn_{id(object())}"
    
    try:
        yield transaction_id
        print(f"提交事务: {transaction_id}")
    except Exception as e:
        print(f"回滚事务: {transaction_id}, 原因: {e}")
        raise

# 使用@contextmanager创建的上下文管理器
with simple_timer("测试操作"):
    time.sleep(0.1)
    print("执行一些操作")

class TestObj:
    def __init__(self):
        self.value = 42

test_obj = TestObj()
print(f"\n原始值: {test_obj.value}")
with temporary_attribute(test_obj, "value", 100) as obj:
    print(f"临时值: {obj.value}")
print(f"恢复值: {test_obj.value}")

print("\n数据库事务演示:")
try:
    with database_transaction() as txn_id:
        print(f"在事务 {txn_id} 中执行操作")
        # raise ValueError("模拟错误")  # 取消注释测试回滚
except ValueError as e:
    print(f"处理异常: {e}")

contextlib的实用工具

# contextlib实用工具
print("\n=== contextlib实用工具 ===")

# 2. ExitStack - 动态管理多个上下文管理器
print("\n2. ExitStack:")

def process_files(filenames):
    """使用ExitStack处理多个文件"""
    with ExitStack() as stack:
        files = []
        for filename in filenames:
            # 动态添加上下文管理器
            file_obj = stack.enter_context(open(filename, 'w'))
            files.append(file_obj)
        
        # 所有文件都已打开,现在可以使用它们
        for i, file_obj in enumerate(files):
            file_obj.write(f"这是文件 {i+1} 的内容\n")
        
        print(f"成功处理了 {len(files)} 个文件")
        return files
    # 所有文件在这里自动关闭

# 创建测试文件
test_files = ['test1.txt', 'test2.txt', 'test3.txt']
process_files(test_files)

# 验证文件已关闭
with open('test1.txt', 'r') as f:
    print(f"文件内容: {f.read().strip()}")

# 3. suppress - 抑制指定异常
print("\n3. suppress:")

with suppress(FileNotFoundError):
    os.remove('不存在的文件.txt')
    print("这行不会执行")
print("FileNotFoundError被抑制了")

with suppress(ValueError, TypeError):
    int('不是数字')
    print("这行不会执行")
print("ValueError被抑制了")

# 4. redirect_stdout和redirect_stderr
print("\n4. 重定向标准输出:")

output_buffer = StringIO()
with redirect_stdout(output_buffer):
    print("这个输出被重定向了")
    print("这也是")

print(f"捕获的输出: {repr(output_buffer.getvalue())}")

# 重定向到文件
with open('output.txt', 'w') as f:
    with redirect_stdout(f):
        print("这个输出写入文件")
        print("数字:", 42)

with open('output.txt', 'r') as f:
    print(f"文件内容: {repr(f.read())}")

# 5. 组合使用多个contextlib工具
print("\n5. 组合使用:")

@contextmanager
def capture_and_suppress():
    """捕获输出并抑制异常"""
    output = StringIO()
    with ExitStack() as stack:
        stack.enter_context(redirect_stdout(output))
        stack.enter_context(suppress(ValueError, TypeError))
        
        try:
            yield output
        finally:
            pass

with capture_and_suppress() as output:
    print("正常输出")
    int('错误输入')  # 这个异常会被抑制
    print("继续输出")

print(f"捕获的输出: {repr(output.getvalue())}")

# 6. 临时目录管理
print("\n6. 临时目录管理:")

@contextmanager
def temporary_directory():
    """创建临时目录"""
    temp_dir = tempfile.mkdtemp()
    print(f"创建临时目录: {temp_dir}")
    try:
        yield temp_dir
    finally:
        shutil.rmtree(temp_dir)
        print(f"删除临时目录: {temp_dir}")

with temporary_directory() as temp_dir:
    # 在临时目录中工作
    temp_file = os.path.join(temp_dir, 'temp.txt')
    with open(temp_file, 'w') as f:
        f.write('临时文件内容')
    print(f"创建临时文件: {temp_file}")
    
    # 验证文件存在
    print(f"文件存在: {os.path.exists(temp_file)}")

# 临时目录已被删除
print(f"临时目录已删除")

高级contextlib应用

# 高级contextlib应用
print("\n=== 高级contextlib应用 ===")

from contextlib import asynccontextmanager, nullcontext
import asyncio
from typing import Optional, Union

# 1. 条件上下文管理器
print("\n1. 条件上下文管理器:")

@contextmanager
def conditional_context(condition, context_manager):
    """根据条件决定是否使用上下文管理器"""
    if condition:
        with context_manager as value:
            yield value
    else:
        yield None

# 使用条件上下文管理器
for use_timer in [True, False]:
    print(f"\n使用计时器: {use_timer}")
    with conditional_context(use_timer, simple_timer("条件计时")) as timer:
        time.sleep(0.05)
        if timer:
            print("计时器已启用")
        else:
            print("计时器未启用")

# 2. 嵌套上下文管理器工厂
print("\n2. 嵌套上下文管理器:")

@contextmanager
def nested_contexts(*context_managers):
    """嵌套多个上下文管理器"""
    with ExitStack() as stack:
        results = []
        for cm in context_managers:
            result = stack.enter_context(cm)
            results.append(result)
        yield results

# 使用嵌套上下文管理器
with nested_contexts(
    simple_timer("操作1"),
    simple_timer("操作2"),
    temporary_directory()
) as (timer1, timer2, temp_dir):
    print(f"在嵌套上下文中工作")
    print(f"临时目录: {temp_dir}")
    time.sleep(0.1)

# 3. 资源池上下文管理器
print("\n3. 资源池管理:")

class ResourcePool:
    """资源池"""
    
    def __init__(self, create_resource, max_size=5):
        self.create_resource = create_resource
        self.max_size = max_size
        self.pool = []
        self.in_use = set()
    
    @contextmanager
    def get_resource(self):
        """获取资源的上下文管理器"""
        # 尝试从池中获取资源
        if self.pool:
            resource = self.pool.pop()
        else:
            resource = self.create_resource()
        
        self.in_use.add(resource)
        print(f"获取资源: {resource}")
        
        try:
            yield resource
        finally:
            # 归还资源到池中
            self.in_use.remove(resource)
            if len(self.pool) < self.max_size:
                self.pool.append(resource)
                print(f"归还资源到池: {resource}")
            else:
                print(f"池已满,销毁资源: {resource}")

# 创建资源池
def create_connection():
    return f"Connection_{id(object())}"

connection_pool = ResourcePool(create_connection, max_size=2)

# 使用资源池
print("\n使用资源池:")
with connection_pool.get_resource() as conn1:
    print(f"使用连接1: {conn1}")
    
    with connection_pool.get_resource() as conn2:
        print(f"使用连接2: {conn2}")
        
        with connection_pool.get_resource() as conn3:
            print(f"使用连接3: {conn3}")

print(f"池中剩余资源: {len(connection_pool.pool)}")

# 4. 异步上下文管理器(Python 3.7+)
print("\n4. 异步上下文管理器:")

# 注意:这里只是展示语法,实际运行需要异步环境
class AsyncContextExample:
    """异步上下文管理器示例"""
    
    async def __aenter__(self):
        print("异步进入上下文")
        # 模拟异步操作
        await asyncio.sleep(0.01)
        return self
    
    async def __aexit__(self, exc_type, exc_value, traceback):
        print("异步退出上下文")
        # 模拟异步清理
        await asyncio.sleep(0.01)
        return False

@asynccontextmanager
async def async_timer(name):
    """异步计时器"""
    print(f"异步开始计时: {name}")
    start_time = time.time()
    try:
        yield start_time
    finally:
        end_time = time.time()
        print(f"异步结束计时: {name}, 耗时: {end_time - start_time:.4f}秒")

# 异步上下文管理器的使用示例(需要在异步函数中)
async def async_example():
    async with AsyncContextExample():
        print("在异步上下文中")
        await asyncio.sleep(0.05)
    
    async with async_timer("异步操作"):
        print("执行异步操作")
        await asyncio.sleep(0.1)

# 运行异步示例
print("运行异步上下文管理器示例:")
asyncio.run(async_example())

# 5. nullcontext - 空上下文管理器
print("\n5. nullcontext:")

def process_with_optional_context(use_context=True):
    """可选地使用上下文管理器"""
    context = simple_timer("可选计时") if use_context else nullcontext()
    
    with context:
        print("执行处理逻辑")
        time.sleep(0.05)

print("使用上下文:")
process_with_optional_context(True)

print("\n不使用上下文:")
process_with_optional_context(False)

高级上下文管理器模式

装饰器形式的上下文管理器

# 装饰器形式的上下文管理器
print("\n=== 装饰器形式的上下文管理器 ===")

import functools
from typing import Callable, Any

class ContextDecorator:
    """可以作为装饰器使用的上下文管理器基类"""
    
    def __call__(self, func):
        @functools.wraps(func)
        def wrapper(*args, **kwargs):
            with self:
                return func(*args, **kwargs)
        return wrapper

class TimingContext(ContextDecorator):
    """计时上下文管理器(可作为装饰器)"""
    
    def __init__(self, name: str = "操作"):
        self.name = name
        self.duration = 0
    
    def __enter__(self):
        print(f"开始计时: {self.name}")
        self.start_time = time.time()
        return self
    
    def __exit__(self, exc_type, exc_value, traceback):
        end_time = time.time()
        self.duration = end_time - self.start_time
        print(f"结束计时: {self.name}, 耗时: {self.duration:.4f}秒")
        return False

class LoggingContext(ContextDecorator):
    """日志上下文管理器"""
    
    def __init__(self, operation: str, level: str = "INFO"):
        self.operation = operation
        self.level = level
    
    def __enter__(self):
        print(f"[{self.level}] 开始操作: {self.operation}")
        return self
    
    def __exit__(self, exc_type, exc_value, traceback):
        if exc_type:
            print(f"[ERROR] 操作失败: {self.operation}, 异常: {exc_value}")
        else:
            print(f"[{self.level}] 操作完成: {self.operation}")
        return False

class RetryContext(ContextDecorator):
    """重试上下文管理器"""
    
    def __init__(self, max_retries: int = 3, delay: float = 1.0):
        self.max_retries = max_retries
        self.delay = delay
        self.attempt = 0
    
    def __enter__(self):
        return self
    
    def __exit__(self, exc_type, exc_value, traceback):
        if exc_type and self.attempt < self.max_retries:
            self.attempt += 1
            print(f"操作失败,第{self.attempt}次重试 (最多{self.max_retries}次)")
            time.sleep(self.delay)
            return True  # 抑制异常,继续重试
        
        if exc_type:
            print(f"操作最终失败,已重试{self.attempt}次")
        return False

# 1. 作为上下文管理器使用
print("\n1. 作为上下文管理器使用:")

with TimingContext("数据处理"):
    time.sleep(0.1)
    print("处理数据中...")

with LoggingContext("文件操作", "DEBUG"):
    print("执行文件操作")

# 2. 作为装饰器使用
print("\n2. 作为装饰器使用:")

@TimingContext("函数执行")
@LoggingContext("业务逻辑")
def business_function(x, y):
    """业务函数"""
    print(f"执行业务逻辑: {x} + {y}")
    time.sleep(0.05)
    return x + y

result = business_function(10, 20)
print(f"结果: {result}")

# 3. 重试装饰器
print("\n3. 重试机制:")

@RetryContext(max_retries=2, delay=0.1)
def unreliable_operation():
    """不可靠的操作"""
    import random
    if random.random() < 0.7:  # 70%的概率失败
        raise RuntimeError("操作失败")
    print("操作成功!")
    return "成功结果"

try:
    result = unreliable_operation()
    print(f"最终结果: {result}")
except RuntimeError as e:
    print(f"操作彻底失败: {e}")

上下文管理器链

# 上下文管理器链
print("\n=== 上下文管理器链 ===")

class ContextChain:
    """上下文管理器链"""
    
    def __init__(self, *contexts):
        self.contexts = contexts
        self.entered_contexts = []
    
    def __enter__(self):
        results = []
        try:
            for context in self.contexts:
                result = context.__enter__()
                self.entered_contexts.append(context)
                results.append(result)
            return results
        except Exception:
            # 如果有异常,需要清理已经进入的上下文
            self._cleanup()
            raise
    
    def __exit__(self, exc_type, exc_value, traceback):
        return self._cleanup(exc_type, exc_value, traceback)
    
    def _cleanup(self, exc_type=None, exc_value=None, traceback=None):
        # 按相反顺序退出上下文
        exceptions = []
        for context in reversed(self.entered_contexts):
            try:
                context.__exit__(exc_type, exc_value, traceback)
            except Exception as e:
                exceptions.append(e)
        
        self.entered_contexts.clear()
        
        # 如果有多个异常,抛出第一个
        if exceptions:
            raise exceptions[0]
        
        return False

class ConditionalContext:
    """条件上下文管理器"""
    
    def __init__(self, condition, true_context, false_context=None):
        self.condition = condition
        self.true_context = true_context
        self.false_context = false_context or nullcontext()
        self.active_context = None
    
    def __enter__(self):
        self.active_context = self.true_context if self.condition else self.false_context
        return self.active_context.__enter__()
    
    def __exit__(self, exc_type, exc_value, traceback):
        if self.active_context:
            return self.active_context.__exit__(exc_type, exc_value, traceback)
        return False

class CachedContext:
    """缓存上下文管理器"""
    
    def __init__(self, cache_key, context_factory):
        self.cache_key = cache_key
        self.context_factory = context_factory
        self._cache = {}
    
    def __enter__(self):
        if self.cache_key not in self._cache:
            context = self.context_factory()
            self._cache[self.cache_key] = context
            print(f"创建新的上下文: {self.cache_key}")
        else:
            context = self._cache[self.cache_key]
            print(f"使用缓存的上下文: {self.cache_key}")
        
        return context.__enter__()
    
    def __exit__(self, exc_type, exc_value, traceback):
        context = self._cache.get(self.cache_key)
        if context:
            return context.__exit__(exc_type, exc_value, traceback)
        return False

# 演示上下文管理器链
print("\n1. 上下文管理器链:")

context1 = TimingContext("操作1")
context2 = LoggingContext("操作2")
context3 = simple_timer("操作3")

with ContextChain(context1, context2, context3) as results:
    print(f"链中的上下文结果: {len(results)}")
    time.sleep(0.1)
    print("在链式上下文中执行操作")

print("\n2. 条件上下文管理器:")

for debug_mode in [True, False]:
    print(f"\n调试模式: {debug_mode}")
    with ConditionalContext(
        debug_mode,
        LoggingContext("调试操作", "DEBUG"),
        LoggingContext("生产操作", "INFO")
    ):
        print("执行条件相关的操作")

print("\n3. 缓存上下文管理器:")

def create_expensive_context():
    print("创建昂贵的上下文...")
    return TimingContext("昂贵操作")

# 第一次使用
with CachedContext("expensive", create_expensive_context):
    time.sleep(0.05)
    print("第一次使用")

# 第二次使用(应该使用缓存)
with CachedContext("expensive", create_expensive_context):
    time.sleep(0.05)
    print("第二次使用")

异步上下文管理器进阶

# 异步上下文管理器进阶
print("\n=== 异步上下文管理器进阶 ===")

import asyncio
from contextlib import asynccontextmanager
from typing import AsyncGenerator

class AsyncResourceManager:
    """异步资源管理器"""
    
    def __init__(self, resource_name: str):
        self.resource_name = resource_name
        self.resource = None
    
    async def __aenter__(self):
        print(f"异步获取资源: {self.resource_name}")
        # 模拟异步资源获取
        await asyncio.sleep(0.01)
        self.resource = f"Resource_{self.resource_name}_{id(self)}"
        print(f"资源获取成功: {self.resource}")
        return self.resource
    
    async def __aexit__(self, exc_type, exc_value, traceback):
        print(f"异步释放资源: {self.resource}")
        # 模拟异步资源释放
        await asyncio.sleep(0.01)
        self.resource = None
        print("资源释放完成")
        return False

@asynccontextmanager
async def async_database_transaction():
    """异步数据库事务"""
    transaction_id = f"async_txn_{id(object())}"
    print(f"开始异步事务: {transaction_id}")
    
    try:
        # 模拟事务开始
        await asyncio.sleep(0.01)
        yield transaction_id
        
        # 模拟事务提交
        await asyncio.sleep(0.01)
        print(f"提交异步事务: {transaction_id}")
    except Exception as e:
        # 模拟事务回滚
        await asyncio.sleep(0.01)
        print(f"回滚异步事务: {transaction_id}, 原因: {e}")
        raise

@asynccontextmanager
async def async_connection_pool(max_connections: int = 5):
    """异步连接池"""
    pool = []
    active_connections = set()
    
    async def get_connection():
        if pool:
            conn = pool.pop()
        else:
            # 模拟创建新连接
            await asyncio.sleep(0.01)
            conn = f"AsyncConn_{len(active_connections)}"
        
        active_connections.add(conn)
        return conn
    
    async def return_connection(conn):
        active_connections.remove(conn)
        if len(pool) < max_connections:
            pool.append(conn)
    
    print(f"初始化异步连接池,最大连接数: {max_connections}")
    
    try:
        yield get_connection, return_connection
    finally:
        print(f"关闭异步连接池,活跃连接: {len(active_connections)}")
        # 清理所有连接
        for conn in active_connections:
            print(f"强制关闭连接: {conn}")

async def async_context_examples():
    """异步上下文管理器示例"""
    
    print("\n1. 基本异步上下文管理器:")
    async with AsyncResourceManager("数据库") as resource:
        print(f"使用资源: {resource}")
        await asyncio.sleep(0.05)
    
    print("\n2. 异步事务管理:")
    try:
        async with async_database_transaction() as txn:
            print(f"在事务 {txn} 中执行操作")
            await asyncio.sleep(0.02)
            # raise ValueError("模拟事务错误")  # 取消注释测试回滚
    except ValueError as e:
        print(f"处理事务异常: {e}")
    
    print("\n3. 异步连接池:")
    async with async_connection_pool(3) as (get_conn, return_conn):
        # 获取多个连接
        conn1 = await get_conn()
        conn2 = await get_conn()
        print(f"获取连接: {conn1}, {conn2}")
        
        # 使用连接
        await asyncio.sleep(0.02)
        
        # 归还连接
        await return_conn(conn1)
        await return_conn(conn2)
        print("连接已归还")
    
    print("\n4. 并发异步上下文:")
    async def worker(worker_id):
        async with AsyncResourceManager(f"Worker{worker_id}") as resource:
            print(f"Worker {worker_id} 开始工作")
            await asyncio.sleep(0.1)
            print(f"Worker {worker_id} 完成工作")
            return f"Result_{worker_id}"
    
    # 并发执行多个异步上下文
    tasks = [worker(i) for i in range(3)]
    results = await asyncio.gather(*tasks)
    print(f"并发结果: {results}")

# 运行异步示例
print("运行异步上下文管理器示例:")
asyncio.run(async_context_examples())

实践练习

练习1:配置管理系统

创建一个完整的配置管理系统,支持多层配置、环境变量、配置验证等功能。

# 练习1:配置管理系统
print("\n=== 练习1:配置管理系统 ===")

import json
import os
from typing import Dict, Any, Optional, Union
from contextlib import contextmanager
from copy import deepcopy

class ConfigurationError(Exception):
    """配置错误异常"""
    pass

class ConfigValidator:
    """配置验证器"""
    
    @staticmethod
    def validate_database_config(config: Dict[str, Any]) -> bool:
        """验证数据库配置"""
        required_fields = ['host', 'port', 'database', 'username']
        for field in required_fields:
            if field not in config:
                raise ConfigurationError(f"数据库配置缺少必需字段: {field}")
        
        if not isinstance(config['port'], int) or config['port'] <= 0:
            raise ConfigurationError("数据库端口必须是正整数")
        
        return True
    
    @staticmethod
    def validate_logging_config(config: Dict[str, Any]) -> bool:
        """验证日志配置"""
        valid_levels = ['DEBUG', 'INFO', 'WARNING', 'ERROR', 'CRITICAL']
        level = config.get('level', 'INFO')
        
        if level not in valid_levels:
            raise ConfigurationError(f"无效的日志级别: {level}")
        
        return True

class ConfigManager:
    """配置管理器"""
    
    def __init__(self):
        self.config_stack = []
        self.validators = {
            'database': ConfigValidator.validate_database_config,
            'logging': ConfigValidator.validate_logging_config
        }
    
    def load_from_file(self, filename: str) -> Dict[str, Any]:
        """从文件加载配置"""
        try:
            with open(filename, 'r', encoding='utf-8') as f:
                if filename.endswith('.json'):
                    return json.load(f)
                else:
                    # 简单的key=value格式
                    config = {}
                    for line in f:
                        line = line.strip()
                        if line and not line.startswith('#'):
                            key, value = line.split('=', 1)
                            config[key.strip()] = value.strip()
                    return config
        except FileNotFoundError:
            print(f"配置文件不存在: {filename}")
            return {}
        except Exception as e:
            raise ConfigurationError(f"加载配置文件失败: {e}")
    
    def load_from_env(self, prefix: str = 'APP_') -> Dict[str, Any]:
        """从环境变量加载配置"""
        config = {}
        for key, value in os.environ.items():
            if key.startswith(prefix):
                config_key = key[len(prefix):].lower()
                # 尝试转换数据类型
                if value.isdigit():
                    config[config_key] = int(value)
                elif value.lower() in ('true', 'false'):
                    config[config_key] = value.lower() == 'true'
                else:
                    config[config_key] = value
        return config
    
    def merge_configs(self, *configs: Dict[str, Any]) -> Dict[str, Any]:
        """合并多个配置"""
        merged = {}
        for config in configs:
            self._deep_merge(merged, config)
        return merged
    
    def _deep_merge(self, target: Dict[str, Any], source: Dict[str, Any]):
        """深度合并字典"""
        for key, value in source.items():
            if key in target and isinstance(target[key], dict) and isinstance(value, dict):
                self._deep_merge(target[key], value)
            else:
                target[key] = value
    
    def validate_config(self, config: Dict[str, Any]):
        """验证配置"""
        for section, validator in self.validators.items():
            if section in config:
                try:
                    validator(config[section])
                except ConfigurationError as e:
                    raise ConfigurationError(f"配置验证失败 [{section}]: {e}")
    
    @contextmanager
    def config_context(self, **config_overrides):
        """配置上下文管理器"""
        # 获取当前配置
        current_config = self.get_current_config()
        
        # 应用覆盖配置
        new_config = deepcopy(current_config)
        self._deep_merge(new_config, config_overrides)
        
        # 验证新配置
        self.validate_config(new_config)
        
        # 推入配置栈
        self.config_stack.append(new_config)
        print(f"应用配置覆盖: {config_overrides}")
        
        try:
            yield new_config
        finally:
            # 弹出配置栈
            self.config_stack.pop()
            print("恢复原始配置")
    
    @contextmanager
    def environment_config(self, env_name: str):
        """环境配置上下文管理器"""
        env_configs = {
            'development': {
                'database': {'host': 'localhost', 'port': 5432, 'database': 'dev_db'},
                'logging': {'level': 'DEBUG'},
                'debug': True
            },
            'testing': {
                'database': {'host': 'localhost', 'port': 5433, 'database': 'test_db'},
                'logging': {'level': 'WARNING'},
                'debug': False
            },
            'production': {
                'database': {'host': 'prod-db.example.com', 'port': 5432, 'database': 'prod_db'},
                'logging': {'level': 'ERROR'},
                'debug': False
            }
        }
        
        if env_name not in env_configs:
            raise ConfigurationError(f"未知的环境: {env_name}")
        
        env_config = env_configs[env_name]
        
        with self.config_context(**env_config):
            print(f"切换到环境: {env_name}")
            yield self.get_current_config()
    
    def get_current_config(self) -> Dict[str, Any]:
        """获取当前配置"""
        if self.config_stack:
            return self.config_stack[-1]
        return {}
    
    def get_config_value(self, key: str, default: Any = None) -> Any:
        """获取配置值(支持点号分隔的嵌套键)"""
        config = self.get_current_config()
        keys = key.split('.')
        
        current = config
        for k in keys:
            if isinstance(current, dict) and k in current:
                current = current[k]
            else:
                return default
        
        return current

# 配置管理系统演示
print("\n配置管理系统演示:")

# 创建配置文件
dev_config = {
    "database": {
        "host": "localhost",
        "port": 5432,
        "database": "myapp_dev",
        "username": "dev_user",
        "password": "dev_pass"
    },
    "logging": {
        "level": "DEBUG",
        "file": "app.log"
    },
    "cache": {
        "enabled": True,
        "ttl": 300
    }
}

with open('dev_config.json', 'w') as f:
    json.dump(dev_config, f, indent=2)

# 设置环境变量
os.environ['APP_DEBUG'] = 'true'
os.environ['APP_CACHE_TTL'] = '600'

config_manager = ConfigManager()

# 加载基础配置
base_config = config_manager.load_from_file('dev_config.json')
env_config = config_manager.load_from_env('APP_')

print(f"基础配置: {base_config}")
print(f"环境变量配置: {env_config}")

# 合并配置
merged_config = config_manager.merge_configs(base_config, env_config)
print(f"合并后配置: {merged_config}")

# 使用配置上下文
with config_manager.config_context(**merged_config) as config:
    print(f"\n当前数据库主机: {config_manager.get_config_value('database.host')}")
    print(f"当前日志级别: {config_manager.get_config_value('logging.level')}")
    print(f"缓存TTL: {config_manager.get_config_value('cache.ttl')}")
    
    # 临时覆盖配置
    with config_manager.config_context(
        database={'host': 'test-db.example.com', 'port': 5433},
        logging={'level': 'WARNING'}
    ) as test_config:
        print(f"\n测试环境数据库主机: {config_manager.get_config_value('database.host')}")
        print(f"测试环境日志级别: {config_manager.get_config_value('logging.level')}")
        print(f"测试环境数据库端口: {config_manager.get_config_value('database.port')}")
    
    print(f"\n恢复后数据库主机: {config_manager.get_config_value('database.host')}")

# 环境配置切换
print("\n环境配置切换演示:")
for env in ['development', 'testing', 'production']:
    with config_manager.environment_config(env) as env_config:
        print(f"\n{env.upper()} 环境:")
        print(f"  数据库: {config_manager.get_config_value('database.host')}:{config_manager.get_config_value('database.port')}")
        print(f"  日志级别: {config_manager.get_config_value('logging.level')}")
        print(f"  调试模式: {config_manager.get_config_value('debug')}")

# 清理测试文件
os.remove('dev_config.json')
os.environ.pop('APP_DEBUG', None)
os.environ.pop('APP_CACHE_TTL', None)

练习2:资源池管理系统

创建一个通用的资源池管理系统,支持不同类型的资源、连接限制、超时处理等功能。

# 练习2:资源池管理系统
print("\n=== 练习2:资源池管理系统 ===")

import threading
import queue
import time
from typing import TypeVar, Generic, Callable, Optional, Any
from contextlib import contextmanager
from dataclasses import dataclass
from enum import Enum

T = TypeVar('T')

class ResourceState(Enum):
    """资源状态"""
    AVAILABLE = "available"
    IN_USE = "in_use"
    EXPIRED = "expired"
    ERROR = "error"

@dataclass
class ResourceInfo:
    """资源信息"""
    resource: Any
    created_at: float
    last_used: float
    use_count: int
    state: ResourceState
    
    def is_expired(self, max_age: float) -> bool:
        """检查资源是否过期"""
        return time.time() - self.created_at > max_age
    
    def is_idle_too_long(self, max_idle: float) -> bool:
        """检查资源是否空闲过久"""
        return time.time() - self.last_used > max_idle

class ResourcePoolError(Exception):
    """资源池异常"""
    pass

class ResourcePool(Generic[T]):
    """通用资源池"""
    
    def __init__(
        self,
        factory: Callable[[], T],
        destroyer: Optional[Callable[[T], None]] = None,
        max_size: int = 10,
        min_size: int = 1,
        max_age: float = 3600,  # 1小时
        max_idle: float = 300,  # 5分钟
        acquire_timeout: float = 30
    ):
        self.factory = factory
        self.destroyer = destroyer or (lambda x: None)
        self.max_size = max_size
        self.min_size = min_size
        self.max_age = max_age
        self.max_idle = max_idle
        self.acquire_timeout = acquire_timeout
        
        self._pool: queue.Queue[ResourceInfo] = queue.Queue(maxsize=max_size)
        self._in_use: dict[T, ResourceInfo] = {}
        self._lock = threading.RLock()
        self._total_created = 0
        self._total_destroyed = 0
        
        # 预创建最小数量的资源
        self._initialize_pool()
        
        # 启动清理线程
        self._cleanup_thread = threading.Thread(target=self._cleanup_worker, daemon=True)
        self._cleanup_thread.start()
    
    def _initialize_pool(self):
        """初始化资源池"""
        for _ in range(self.min_size):
            try:
                resource = self._create_resource()
                resource_info = ResourceInfo(
                    resource=resource,
                    created_at=time.time(),
                    last_used=time.time(),
                    use_count=0,
                    state=ResourceState.AVAILABLE
                )
                self._pool.put_nowait(resource_info)
            except Exception as e:
                print(f"初始化资源失败: {e}")
    
    def _create_resource(self) -> T:
        """创建新资源"""
        with self._lock:
            if self._total_created >= self.max_size:
                raise ResourcePoolError("资源池已达到最大容量")
            
            resource = self.factory()
            self._total_created += 1
            print(f"创建新资源: {resource} (总计: {self._total_created})")
            return resource
    
    def _destroy_resource(self, resource_info: ResourceInfo):
        """销毁资源"""
        try:
            self.destroyer(resource_info.resource)
            with self._lock:
                self._total_destroyed += 1
            print(f"销毁资源: {resource_info.resource} (总计: {self._total_destroyed})")
        except Exception as e:
            print(f"销毁资源失败: {e}")
    
    def _cleanup_worker(self):
        """清理工作线程"""
        while True:
            try:
                time.sleep(60)  # 每分钟清理一次
                self._cleanup_expired_resources()
            except Exception as e:
                print(f"清理线程异常: {e}")
    
    def _cleanup_expired_resources(self):
        """清理过期资源"""
        expired_resources = []
        
        # 检查池中的资源
        temp_resources = []
        while not self._pool.empty():
            try:
                resource_info = self._pool.get_nowait()
                if (resource_info.is_expired(self.max_age) or 
                    resource_info.is_idle_too_long(self.max_idle)):
                    expired_resources.append(resource_info)
                else:
                    temp_resources.append(resource_info)
            except queue.Empty:
                break
        
        # 将未过期的资源放回池中
        for resource_info in temp_resources:
            try:
                self._pool.put_nowait(resource_info)
            except queue.Full:
                expired_resources.append(resource_info)
        
        # 销毁过期资源
        for resource_info in expired_resources:
            self._destroy_resource(resource_info)
        
        if expired_resources:
            print(f"清理了 {len(expired_resources)} 个过期资源")
    
    @contextmanager
    def acquire(self):
        """获取资源的上下文管理器"""
        resource_info = None
        start_time = time.time()
        
        try:
            # 尝试从池中获取资源
            while resource_info is None:
                try:
                    resource_info = self._pool.get(timeout=1.0)
                    break
                except queue.Empty:
                    # 池为空,尝试创建新资源
                    try:
                        resource = self._create_resource()
                        resource_info = ResourceInfo(
                            resource=resource,
                            created_at=time.time(),
                            last_used=time.time(),
                            use_count=0,
                            state=ResourceState.AVAILABLE
                        )
                    except ResourcePoolError:
                        # 无法创建新资源,检查超时
                        if time.time() - start_time > self.acquire_timeout:
                            raise ResourcePoolError(f"获取资源超时 ({self.acquire_timeout}秒)")
                        continue
            
            # 更新资源状态
            resource_info.state = ResourceState.IN_USE
            resource_info.last_used = time.time()
            resource_info.use_count += 1
            
            with self._lock:
                self._in_use[resource_info.resource] = resource_info
            
            print(f"获取资源: {resource_info.resource} (使用次数: {resource_info.use_count})")
            yield resource_info.resource
            
        except Exception as e:
            if resource_info:
                resource_info.state = ResourceState.ERROR
            raise
        
        finally:
            if resource_info:
                # 归还资源到池中
                with self._lock:
                    self._in_use.pop(resource_info.resource, None)
                
                if resource_info.state == ResourceState.ERROR:
                    # 错误状态的资源直接销毁
                    self._destroy_resource(resource_info)
                else:
                    # 正常归还到池中
                    resource_info.state = ResourceState.AVAILABLE
                    resource_info.last_used = time.time()
                    
                    try:
                        self._pool.put_nowait(resource_info)
                        print(f"归还资源: {resource_info.resource}")
                    except queue.Full:
                        # 池已满,销毁资源
                        self._destroy_resource(resource_info)
    
    def get_stats(self) -> dict:
        """获取资源池统计信息"""
        with self._lock:
            return {
                'pool_size': self._pool.qsize(),
                'in_use': len(self._in_use),
                'total_created': self._total_created,
                'total_destroyed': self._total_destroyed,
                'max_size': self.max_size,
                'min_size': self.min_size
            }
    
    def close(self):
        """关闭资源池"""
        print("关闭资源池...")
        
        # 销毁池中的所有资源
        while not self._pool.empty():
            try:
                resource_info = self._pool.get_nowait()
                self._destroy_resource(resource_info)
            except queue.Empty:
                break
        
        # 销毁正在使用的资源
        with self._lock:
            for resource_info in list(self._in_use.values()):
                self._destroy_resource(resource_info)
            self._in_use.clear()

# 数据库连接模拟
class MockDatabaseConnection:
    """模拟数据库连接"""
    
    _connection_counter = 0
    
    def __init__(self):
        MockDatabaseConnection._connection_counter += 1
        self.id = MockDatabaseConnection._connection_counter
        self.connected = True
        print(f"  -> 建立数据库连接 {self.id}")
    
    def execute(self, sql: str):
        """执行SQL"""
        if not self.connected:
            raise RuntimeError("连接已关闭")
        print(f"  -> 连接 {self.id} 执行: {sql}")
        return f"Result from connection {self.id}"
    
    def close(self):
        """关闭连接"""
        if self.connected:
            self.connected = False
            print(f"  -> 关闭数据库连接 {self.id}")
    
    def __str__(self):
        return f"DBConn_{self.id}"

# 资源池演示
print("\n资源池管理系统演示:")

# 创建数据库连接池
db_pool = ResourcePool(
    factory=MockDatabaseConnection,
    destroyer=lambda conn: conn.close(),
    max_size=5,
    min_size=2,
    max_age=10,  # 10秒过期(演示用)
    max_idle=5,  # 5秒空闲过期
    acquire_timeout=5
)

print(f"\n初始状态: {db_pool.get_stats()}")

# 单个资源使用
print("\n1. 单个资源使用:")
with db_pool.acquire() as conn:
    result = conn.execute("SELECT * FROM users")
    print(f"查询结果: {result}")

print(f"使用后状态: {db_pool.get_stats()}")

# 并发资源使用
print("\n2. 并发资源使用:")

def worker(worker_id, num_operations):
    """工作线程"""
    for i in range(num_operations):
        try:
            with db_pool.acquire() as conn:
                conn.execute(f"Worker {worker_id} - Operation {i+1}")
                time.sleep(0.1)  # 模拟操作耗时
        except Exception as e:
            print(f"Worker {worker_id} 错误: {e}")

# 启动多个工作线程
threads = []
for i in range(3):
    thread = threading.Thread(target=worker, args=(i+1, 2))
    threads.append(thread)
    thread.start()

# 等待所有线程完成
for thread in threads:
    thread.join()

print(f"\n并发使用后状态: {db_pool.get_stats()}")

# 测试资源池限制
print("\n3. 测试资源池限制:")
connections = []
try:
    # 尝试获取超过最大数量的连接
    for i in range(7):  # 超过max_size=5
        conn_context = db_pool.acquire()
        conn = conn_context.__enter__()
        connections.append((conn_context, conn))
        print(f"获取连接 {i+1}: {conn}")
except ResourcePoolError as e:
    print(f"资源池限制: {e}")
finally:
    # 释放所有连接
    for conn_context, conn in connections:
        try:
            conn_context.__exit__(None, None, None)
        except:
            pass

print(f"\n释放后状态: {db_pool.get_stats()}")

# 等待一段时间让清理线程工作
print("\n4. 等待资源清理...")
time.sleep(6)  # 等待资源过期
print(f"清理后状态: {db_pool.get_stats()}")

# 关闭资源池
db_pool.close()
print(f"\n关闭后状态: {db_pool.get_stats()}")

总结

核心知识点

  1. 上下文管理器协议

    • __enter____exit__方法
    • 异常处理和资源清理
    • 返回值和异常抑制
  2. contextlib模块

    • @contextmanager装饰器
    • ExitStack动态管理
    • suppress异常抑制
    • 输出重定向工具
  3. 高级模式

    • 装饰器形式的上下文管理器
    • 上下文管理器链和组合
    • 条件和缓存上下文管理器
    • 异步上下文管理器
  4. 实际应用

    • 资源管理(文件、数据库、网络)
    • 状态管理(配置、环境、属性)
    • 性能监控和调试
    • 事务和锁管理

技能掌握

🟢 基础级别

  • 理解上下文管理器的概念和作用
  • 使用with语句进行文件操作
  • 实现简单的自定义上下文管理器
  • 使用@contextmanager装饰器

🟡 中级级别

  • 掌握异常处理和资源清理机制
  • 使用contextlib模块的各种工具
  • 实现复杂的资源管理上下文管理器
  • 理解上下文管理器的执行流程

🔴 高级级别

  • 设计可重用的上下文管理器模式
  • 实现异步上下文管理器
  • 创建上下文管理器链和组合
  • 优化上下文管理器的性能

最佳实践

设计原则

  • 确保资源的正确获取和释放
  • 提供清晰的错误处理机制
  • 支持嵌套和组合使用
  • 保持接口的简洁性

性能考虑

  • 避免在__enter____exit__中执行耗时操作
  • 合理使用资源池和缓存
  • 考虑并发安全性
  • 优化异常处理路径

错误处理

  • __exit__中正确处理异常
  • 提供有意义的错误信息
  • 确保资源清理的可靠性
  • 考虑异常传播和抑制

可维护性

  • 使用描述性的类名和方法名
  • 提供完整的文档和示例
  • 支持配置和定制
  • 遵循单一职责原则

常见陷阱

资源管理

  • 忘记在__exit__中释放资源
  • 异常情况下的资源泄漏
  • 重复释放资源导致错误
  • 不正确的异常处理

并发问题

  • 线程安全性考虑不足
  • 死锁和竞态条件
  • 资源竞争和饥饿
  • 异步上下文的同步问题

性能问题

  • 过度创建和销毁资源
  • 不必要的同步开销
  • 内存泄漏和引用循环
  • 异常处理的性能影响

设计问题

  • 上下文管理器职责过重
  • 缺乏适当的抽象层次
  • 硬编码的配置和限制
  • 不支持嵌套和组合

性能考虑

资源开销

  • 上下文管理器的创建和销毁成本
  • 资源获取和释放的时间
  • 内存使用和垃圾回收影响
  • 异常处理的性能开销

优化策略

  • 使用资源池减少创建销毁开销
  • 实现资源的延迟初始化
  • 优化异常处理路径
  • 考虑使用__slots__减少内存使用

监控和调试

  • 添加性能监控和统计
  • 提供调试和诊断工具
  • 记录资源使用情况
  • 分析瓶颈和优化点

下一步学习

  1. 闭包与作用域 - 理解Python的作用域规则和闭包机制
  2. lambda表达式与函数式编程 - 掌握函数式编程范式
  3. 异步编程与协程 - 深入学习异步上下文管理器
  4. 多线程与并发编程 - 理解并发环境下的上下文管理

扩展阅读


更新时间: 2024-12-19
版本: v1.0.0
作者: Python教程团队

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

lvjesus

码力充电

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值