# Summary 装饰器模式允许你在不修改原有对象的情况下,动态地给对象添加新的功能。它通过创建一个包装对象(装饰器)来包裹真实对象。比如大模型工程添加[[langfuse]],不需要怎么改代码,加几个装饰器就好了 基础的一 shot 意式浓缩,加不同的东西就成了不同的 [咖啡](咖啡.md) <!-- more --> ## 场景 基础的一 shot 意式浓缩,加不同的东西就成了不同的咖啡,基酒也是如此。 | | 意式浓缩 | 水 | 牛奶 | 奶油 | 巧克力 | | ---- | ---- | --- | --- | --- | --- | | 美式 | ✔️ | ✔️ | | | | | 摩卡 | ✔️ | | ✔️ | ✔️ | ✔️ | | 拿铁 | ✔️ | | ✔️ | | | | 卡布奇诺 | ✔️ | | ✔️ | ✔️ | | ## 实现 JavaIO 中基于基本的 inputstream 可以生成多种 file、object 等 stream ![image.png|1000](https://imagehosting4picgo.oss-cn-beijing.aliyuncs.com/imagehosting/fix-dir%2Fpicgo%2Fpicgo-clipboard-images%2F2024%2F05%2F10%2F11-45-09-e4dfc1a04daa6dd2f58409a9721bcb43-20240510114507-3dc3e2.png) ```python import os from contextlib import contextmanager from sqlalchemy import create_engine from sqlalchemy.orm import sessionmaker, scoped_session from sqlalchemy.pool import QueuePool from dotenv import load_dotenv class DatabaseManager: _instance = None # 用于实现单例模式 def __new__(cls): # 单例模式实现:确保只创建一个 DatabaseManager 实例 if cls._instance is None: cls._instance = super(DatabaseManager, cls).__new__(cls) cls._instance._initialize() return cls._instance def _initialize(self): # 初始化数据库连接 load_dotenv() # 加载 .env 文件中的环境变量 db_url = os.getenv('DATABASE_URL') if db_url is None: raise ValueError("DATABASE_URL not found in environment variables.") # 创建数据库引擎,配置连接池 self.engine = create_engine(db_url, poolclass=QueuePool, pool_size=5, max_overflow=10) # 创建线程安全的会话工厂 self.SessionFactory = scoped_session(sessionmaker(bind=self.engine)) @contextmanager def session_scope(self): # 上下文管理器,用于管理数据库会话 session = self.SessionFactory() try: yield session # 将会话提供给调用者 session.commit() # 如果没有异常,提交事务 except: session.rollback() # 如果有异常,回滚事务 raise finally: session.close() # 无论如何,确保关闭会话 # 创建 DatabaseManager 的全局实例 db_manager = DatabaseManager() ``` 您的观察非常敏锐。这个 `session_scope` 方法确实体现了一种类似于面向切面编程(Aspect-Oriented Programming, AOP)的思想。让我详细解释一下: 1. 切面编程的本质: AOP 的核心思想是将横切关注点(cross-cutting concerns)从主要业务逻辑中分离出来。在这个例子中,数据库会话的管理就是一个典型的横切关注点。 2. `session_scope` 作为切面: - 它封装了会话创建、提交、回滚和关闭的逻辑。 - 这些操作对于任何使用数据库的代码都是通用的,但不直接关联于特定的业务逻辑。 3. 装饰器模式: `@contextmanager` 装饰器使得 `session_scope` 成为一个生成器函数,可以用作上下文管理器。这种模式允许在进入和退出某个代码块时自动执行特定的操作。 4. 关注点分离: - 主要业务逻辑(在 `with` 块内的代码)不需要关心会话的生命周期管理。 - 会话管理的细节被封装在 `session_scope` 中,实现了关注点的分离。 5. 横切性质: 这种方法可以应用于任何需要数据库会话的操作,不限于特定的业务功能,体现了横切性质。 6. 一致性和可重用性: 通过这种方式,可以确保所有数据库操作都遵循相同的会话管理模式,提高了代码的一致性和可重用性。 让我们通过一个例子来进一步说明这种 AOP 思想: ```python # 使用 session_scope 的示例 class UserService: def create_user(self, username, email): with db_manager.session_scope() as session: new_user = User(username=username, email=email) session.add(new_user) # 会话管理由 session_scope 处理,而不是在这里显式地管理 def get_user(self, user_id): with db_manager.session_scope() as session: return session.query(User).filter(User.id == user_id).first() # 同样,会话的提交或回滚由 session_scope 处理 # 在这个例子中,UserService 的方法只关注业务逻辑 # 会话的生命周期管理被 "切面" (session_scope) 处理 ``` 在这个例子中: 1. 业务逻辑(创建用户、获取用户)与会话管理完全分离。 2. `session_scope` 作为一个 "切面",横跨所有需要数据库操作的方法。 3. 开发者可以专注于编写业务逻辑,而不必重复编写会话管理代码。 这种方法的优势包括: - 代码复用:会话管理逻辑只需要在一个地方定义。 - 一致性:所有数据库操作都以相同的方式处理会话。 - 可维护性:如果需要更改会话管理逻辑,只需要修改 `session_scope` 方法。 - 降低错误风险:自动化的会话管理减少了忘记提交或回滚事务的风险。 虽然 Python 没有像某些语言(如 Java 的 Spring AOP)那样的正式 AOP 框架,但这种使用上下文管理器的方法实现了类似的效果,体现了 AOP 的核心思想。 你对这种方法在实际项目中的应用有什么想法吗?或者你想了解如何将这种思想扩展到其他领域? 装饰器是啥设计模式 核心概念 Python 中的装饰器 Python 的 `@decorator` 语法是装饰器模式的语法糖实现: ```python # 基本装饰器 def logger(func): def wrapper(*args, **kwargs): print(f"调用函数: {func.__name__}") result = func(*args, **kwargs) print(f"函数返回: {result}") return result return wrapper @logger def add(a, b): return a + b ``` # 等价于: $add = logger(add)$ 在你的代码中的应用 ```python @langfuse_trace("segmentation_workflow") def main_process(self, sentence: str): # 原有逻辑不变 pass ``` 这里 `@langfuse_trace` 装饰器: 1. 包装原函数 - 不修改 `main_process` 的代码 2. 添加功能 - 自动记录函数调用、参数、返回值、执行时间 3. 透明执行 - 调用方式不变,但增加了观测性 装饰器模式的优势 4. 开闭原则 - 对扩展开放,对修改关闭 5. 单一职责 - 业务逻辑和横切关注点(如日志、监控)分离 6. 可组合 - 可以叠加多个装饰器 7. 动态性 - 运行时决定是否添加功能 这就是为什么选择装饰器来实现 Langfuse 集成 - 既能添加观测性,又不破坏原有代码结构。