Skip to content

Latest commit

 

History

History

Folders and files

NameName
Last commit message
Last commit date

parent directory

..
 
 

README.md

ABC

ABC 是 Python 标准库 abc 模块中的一个类,全称是 "Abstract Base Class"(抽象基类)。 主要作用:

  • 定义抽象接口:ABC 用于创建不能被直接实例化的抽象类
  • 强制子类实现:通过 @abstractmethod 装饰器标记的方法必须在子类中被实现
  • 供模板:为子类提供通用的接口规范和部分实现
class Condenser(ABC):
    @abstractmethod
    def condense(self, View) -> View | Condensation:
        """这个方法必须在子类中实现"""
        pass

优势:

  • 接口一致性:确保所有 Condenser 的子类都实现相同的接口
  • 设计规范:强制开发者遵循预定义的架构模式
  • 类型安全:提供更好的代码提示和静态检查支持

通过使用 ABC,Condenser 类成为一个抽象基类,定义了所有具体 condenser 实现必须遵循的接口规范。

@abstractmethod

基本概念

  • 抽象方法标记:@abstractmethod 是 Python abc 模块提供的装饰器,用于标记抽象方法
  • 强制实现:被标记的方法必须在子类中被实现,否则会抛出 TypeError
@abstractmethod
def condense(self, View) -> View | Condensation:
    """Condense a sequence of events into a potentially smaller list."""

主要特点

  1. 接口规范:
    • 定义了所有 Condenser 子类必须实现的标准接口
    • 确保不同的压缩策略都提供相同的调用方式
  2. 设计约束:
    • 强制子类实现具体的压缩逻辑
    • 提供统一的返回类型 (View | Condensation)
  3. 文档说明:
    • 明确告知开发者如何实现自己的压缩策略
    • 指出可以调用 self.add_metadata 记录诊断信息

实际效果

  • 如果某个类继承了 Condenser 但没有实现 condense 方法,实例化时会报错
  • 确保了多态性,不同压缩器可以以相同方式调用 condense 方法

这种方式提供了良好的架构设计,既保证了接口一致性,又允许灵活的具体实现。

BaseModel

Pydantic BaseModel 的特点

from pydantic import BaseModel
  • 数据验证:BaseModel 基于类型提示提供自动数据验证,确保传入的数据符合指定的类型和约束条件。
  • 序列化/反序列化:内置 JSON 序列化和反序列化功能,支持 model_dump() 和 model_validate_json() 等方法。
  • 类型注解支持:充分利用 Python 类型提示,提供运行时类型检查和 IDE 自动补全支持。
  • 默认值和可选字段:支持字段默认值、可选字段以及复杂的嵌套数据结构。
  • 配置选项:可通过 model_config 进行各种配置,如别名、忽略额外字段等。

使用对比:传统类 vs BaseModel 继承类

不使用 BaseModel 的传统实现

class TraditionalView:
    def __init__(self, events=None, unhandled_condensation_request=False):
        # 需要手动处理默认值
        self.events = events or []
        self.unhandled_condensation_request = unhandled_condensation_request
        
        # 需要手动添加数据验证
        if not isinstance(self.events, list):
            raise TypeError("events must be a list")
        if not isinstance(self.unhandled_condensation_request, bool):
            raise TypeError("unhandled_condensation_request must be a boolean")
    
    # 需要手动实现序列化方法
    def to_dict(self):
        return {
            'events': [event.__dict__ for event in self.events],
            'unhandled_condensation_request': self.unhandled_condensation_request
        }
    
    @classmethod
    def from_dict(cls, data):
        # 需要手动实现反序列化
        return cls(
            events=data.get('events', []),
            unhandled_condensation_request=data.get('unhandled_condensation_request', False)
        )

使用 BaseModel 的实现

from pydantic import BaseModel

class View(BaseModel):
    """Linearly ordered view of events."""
    
    events: list[Event]  # 自动类型验证
    unhandled_condensation_request: bool = False  # 默认值和类型提示
    
    # 自动获得以下功能:
    # 1. 数据验证
    # 2. JSON序列化/反序列化
    # 3. 类型检查
    # 4. 自动生成__init__方法

主要区别

  1. 数据验证
    • 传统方式:需要手动编写验证逻辑
    • BaseModel:基于类型提示自动验证
  2. 序列化支持
    • 传统方式:需要手动实现 to_dict() 等方法
    • BaseModel:自带 model_dump(), model_dump_json() 方法
  3. 代码简洁性
    • 传统方式:需要大量样板代码
    • BaseModel:声明式定义,代码更简洁
  4. 错误处理
    • 传统方式:需要自己处理各种边界情况
    • BaseModel:提供详细的验证错误信息

在 View 类中使用 BaseModel 可以确保 events 字段始终是有效的 Event 列表,且 unhandled_condensation_request 始终是布尔值,同时获得了便捷的序列化能力。

@model_serializer

覆盖类默认的系列化规则,可以自定义序列化规则输出。

可以序列化json字符串返回,也可以序列化一个dict、list类型返回,等等其他类型的数据。

Pydantic 装饰器: @model_serializer 是 Pydantic V2 提供的一个装饰器,用于自定义 BaseModel 子类的序列化行为 主要用途:

  • 替换或包装模型的默认序列化逻辑
  • 允许开发者精确控制模型如何转换为字典或 JSON 格式
  • 特别适用于需要特定数据结构的 API 交互场景

模式参数:

  • mode='plain': 完全替换默认序列化行为
  • mode='wrap': 在默认序列化基础上进行包装

在 TextContent 类中的应用:

  • 通过 @model_serializer(mode='plain') 装饰 serialize_model 方法
  • 自定义序列化逻辑,根据 cache_prompt 属性决定是否添加缓存控制信息
  • 返回符合 API 要求的数据结构格式

这种机制使得模型能够灵活地适配不同的序列化需求,特别是在与外部系统交互时提供必要的数据格式转换能力。

Plain Mode

  • 用途: 完全覆盖默认的序列化行为
  • 实现方式: 方法除了 self 参数外不接收其他参数
  • 返回值: 必须提供完整的序列化输出
  • 适用场景: 需要完全自定义序列化格式时
from pydantic import BaseModel, model_serializer

class User(BaseModel):
    name: str
    age: int
    
    @model_serializer(mode='plain')
    def serialize_model(self):
        # 返回完全自定义的格式
        return {'custom': f"{self.name}:{self.age}"}

# 使用示例
user = User(name="Alice", age=30)
print(user.model_dump())  # {'custom': 'Alice:30'}

Wrap Mode

  • 用途: 在默认序列化结果基础上进行修改
  • 实现方式: 方法接收一个 handler 参数
  • Handler函数: 调用时执行默认序列化过程
  • 返回值: 可以修改或扩展默认结果
from pydantic import BaseModel, model_serializer

class User(BaseModel):
    name: str
    age: int
    
    @model_serializer(mode='wrap')
    def serialize_model(self, handler):
        # 获取默认序列化结果
        default_result = handler(self)
        # 添加额外信息
        default_result['summary'] = f"{self.name} is {self.age} years old"
        return default_result

# 使用示例
user = User(name="Alice", age=30)
print(user.model_dump())  
# {'name': 'Alice', 'age': 30, 'summary': 'Alice is 30 years old'}

主要区别

  1. 控制级别:
    • plain: 对输出格式拥有完全控制权
    • wrap: 扩展或修改现有行为
  2. 复杂度:
    • plain: 实现更简单但需要承担更多责任
    • wrap: 实现更复杂但可利用默认行为
  3. 灵活性:
    • plain: 最适合完全自定义序列化需求
    • wrap: 最适合增强标准序列化功能

Field

Field 是 Pydantic 框架提供的一个函数,用于在 BaseModel 中定义字段时提供额外的配置和元数据。 主要功能:

  • 设置默认值:通过 default 参数为字段指定默认值,如 Field(default=False)
  • 添加字段描述:可以为字段添加文档说明
  • 配置验证选项:设置字段的验证规则和约束条件
  • 控制序列化行为:定义字段如何被序列化和反序列化
cli_mode: bool = Field(default=False)
llm_config: str | None = Field(default=None)
enable_browsing: bool = Field(default=True)

使用优势:

  • 类型安全:与 Pydantic 的验证系统紧密集成
  • 灵活性:支持丰富的字段配置选项
  • 文档生成:便于生成 JSON Schema 和 API 文档
  • 安全处理可变类型:正确处理列表、字典等可变默认值

在 AgentConfig 类中,Field 被用来定义各种配置项,确保每个字段都有适当的默认值和配置。

通过 Field 函数,开发者可以精确控制每个配置字段的行为和默认值,同时保持代码的清晰性和可维护性。

@dataclass

主要功能

  • 自动生成特殊方法:自动为类生成 init、repr、eq 等方法,减少样板代码
  • 基于类属性定义:根据类中定义的字段自动生成相应的初始化方法和其它魔法方法
@dataclass
class Event:
    # 类字段定义会自动生成对应的 __init__ 方法
    pass

主要优势

  • 代码简洁:无需手动编写构造函数和基本方法
  • 类型安全:结合类型提示提供更好的IDE支持和静态检查
  • 可配置性:支持多种参数配置,如 frozen=True 创建不可变对象

与 BaseModel 的区别

  • @dataclass 是 Python 标准库的一部分,提供基础的数据类功能
  • BaseModel 是 Pydantic 库提供的,提供更多高级功能如数据验证、序列化等
  • Event 类使用 @dataclass 是因为它是基础数据结构,而 View 类使用 BaseModel 需要更复杂的数据验证功能

BaseModel 和 @dataclass 是两种不同的机制,虽然有一些相似的功能,但 BaseModel 提供了更多高级特性。 相似之处

  • 都能自动生成 init 方法,简化对象初始化
  • 都能自动生成 repr 方法,提供对象的字符串表示
  • 都支持字段默认值和类型提示

BaseModel 的优势

  1. 数据验证:
    • BaseModel 支持运行时类型检查和数据验证
    • 可以定义复杂的验证规则
  2. 序列化支持:
    • 内置 model_dump(), model_json() 等方法
    • 支持 JSON 序列化和反序列化
  3. 嵌套模型:
    • 更好地处理嵌套数据结构
    • 支持复杂的数据模型关系
  4. 字段控制:
    • 更灵活的字段配置选项
    • 支持别名、排除、只读等属性
  5. ORM 集成:
    • 更容易与数据库模型集成
    • 支持更多企业级应用特性

总的来说,BaseModel 比 @dataclass 更强大,特别适合构建复杂的、需要数据验证的应用程序。但在简单场景下,@dataclass 可能更轻量级。

field

field 是 Python dataclasses 模块提供的一个函数,用于指定 dataclass 类中字段的特殊配置。

field 的作用:

  • 配置字段行为:允许为 dataclass 字段设置默认值、工厂函数等特殊属性
  • 避免可变默认值问题:通过 default_factory 参数安全地创建可变类型的默认值

这里 field(default_factory=dict) 的含义:

  1. 为 extra_data 字段设置了一个工厂函数 dict
  2. 每次创建新实例时,都会调用 dict() 来生成一个新的空字典作为默认值
  3. 避免了直接使用 extra_data: dict[str, Any] = {} 可能导致的所有实例共享同一个字典对象的问题

为什么需要这样做:

# 错误的方式 - 会导致所有实例共享同一个字典
extra_data: dict[str, Any] = {}

# 正确的方式 - 使用 field(default_factory=dict)
extra_data: dict[str, Any] = field(default_factory=dict)

这样确保每个 State 实例都有自己的独立 extra_data 字典,避免数据污染。

在 Python 的 dataclass 中,以下可变数据类型如果不使用 field(default_factory=...) 而直接赋默认值,会出现共享问题:

  1. dict: 字典类型,所有实例会共享同一个字典对象
  2. list: 列表类型,所有实例会共享同一个列表对象
  3. set: 集合类型,所有实例会共享同一个集合对象
  4. 自定义可变对象: 任何用户自定义的可变类实例,如果直接作为默认值,也会被所有实例共享
# 错误的做法 - 会导致共享问题
@dataclass
class BadExample:
    shared_dict: dict = {}
    shared_list: list = []
    shared_set: set = set()

# 正确的做法 - 使用 field(default_factory=...)
@dataclass
class GoodExample:
    safe_dict: dict = field(default_factory=dict)
    safe_list: list = field(default_factory=list)
    safe_set: set = field(default_factory=set)

对于不可变类型(如 str, int, float, bool, tuple 等),可以直接赋默认值而不会出现共享问题。

可变类型 (Mutable Types) vs 不可变类型 (Immutable Types)

可变类型 (Mutable Types)

  • 定义:创建后可以修改其内容的对象
  • 特点:
    • 可以在原地修改(添加、删除、更改元素)
    • 修改后对象的内存地址不变
    • 多个变量引用同一对象时,修改会影响所有引用
  • 常见可变类型:
    • dict (字典)
    • list (列表)
    • set (集合)
    • 自定义类的实例(通常)

不可变类型 (Immutable Types)

  • 定义:创建后不能修改其内容的对象
  • 特点:
    • 任何"修改"操作都会创建新的对象
    • 原对象保持不变
    • 多个变量引用同一对象时,不会有意外的修改传播
  • 常见不可变类型:
    • str (字符串)
    • int (整数)
    • float (浮点数)
    • bool (布尔值)
    • tuple (元组)
    • frozenset (冻结集合)

在 dataclass 中的影响

  • 可变类型默认值问题:如果直接给可变类型赋默认值,所有实例会共享同一个对象
  • 解决方案:使用 field(default_factory=...) 为每个实例创建独立的对象副本

@classmethod

@classmethod是Python中的一个内置装饰器,它将一个方法标记为类方法。类方法与普通实例方法不同,它们接收类本身作为第一个参数(通常命名为cls),而不是实例(self)。

class MyClass:
    class_variable = "I'm a class variable"
    
    @classmethod
    def my_class_method(cls, param1, param2):
        # cls 指向类本身,而不是类的实例
        print(f"Class is: {cls}")
        print(f"Class variable: {cls.class_variable}")
        return cls.class_variable + " - modified"

主要特点

  1. 第一个参数是类本身:类方法的第一个参数是cls,代表类本身,不是实例。
  2. 可以通过类或实例调用:类方法既可以通过类名直接调用,也可以通过实例调用。
  3. 无法访问实例变量:类方法不能直接访问实例变量(即self.xxx形式的变量)

@staticmethod

基本概念

  • 静态方法装饰器:@staticmethod 用于定义类中的静态方法
  • 无需实例访问:静态方法不接收 self 或 cls 参数,无法访问类或实例的属性

主要特点

  1. 独立性:
    • 不依赖类实例或类本身的状态
    • 可以通过类名直接调用,无需创建实例
  2. 功能定位:
    • 通常用于与类相关但不需访问类数据的工具函数
    • 逻辑上属于类的命名空间,但功能相对独立
@staticmethod
def restore_from_session(
    sid: str, file_store: FileStore, user_id: str | None = None
) -> 'State':
    """Restores the state from the previously saved session."""
    # 实现从会话恢复状态的逻辑

使用优势

  • 调用便利:可以直接通过 State.restore_from_session(...) 调用
  • 语义清晰:表明这是一个与类相关但独立的工具方法
  • 性能优化:避免了实例化开销

与类方法的区别

  • @staticmethod:不能访问 cls 或 self
  • @classmethod:可以访问类本身(cls 参数),常用于替代构造器

这种方式适合用于像 restore_from_session 这样的工厂方法,它创建并返回类实例,但不需要访问现有实例或类的状态。

@contextmanager

Python 的 @contextmanager 是一个非常实用的装饰器,用于将一个生成器函数(generator function)转换为上下文管理器(context manager),从而可以配合 with 语句使用。

它定义在标准库模块 contextlib 中:

from contextlib import contextmanager

🎯 核心思想

通常,要实现一个上下文管理器,你需要定义一个类,并实现 __enter____exit__ 方法。 而 @contextmanager 提供了一种更简洁、函数式的方式来实现同样的功能——只需写一个生成器函数,并在 yield 前后分别写“进入”和“退出”的逻辑。


✅ 基本语法结构

from contextlib import contextmanager

@contextmanager
def my_context():
    # 进入上下文前的准备(相当于 __enter__)
    setup_code()
    try:
        yield resource  # resource 会作为 with as 后的变量值
    finally:
        # 退出上下文时的清理(相当于 __exit__)
        cleanup_code()

⚠️ 必须使用 try...finally:确保即使发生异常,清理代码也能执行。


🔍 示例 1:模拟打开文件(简化版)

1from contextlib import contextmanager
2
3@contextmanager
4def my_open(filename):
5    print(f"打开文件: {filename}")
6    f = open(filename, 'w')
7    try:
8        yield f  # 将文件对象交给 with 语句
9    finally:
10        print("关闭文件")
11        f.close()
12
13# 使用
14with my_open('test.txt') as f:
15    f.write("Hello, @contextmanager!")

输出:

1打开文件: test.txt
2关闭文件

这等价于手动实现的上下文管理器类,但代码更简洁。


🔍 示例 2:计时器上下文管理器

1import time
2from contextlib import contextmanager
3
4@contextmanager
5def timer():
6    start = time.time()
7    try:
8        yield
9    finally:
10        end = time.time()
11        print(f"耗时: {end - start:.4f} 秒")
12
13# 使用
14with timer():
15    time.sleep(1)

输出:

1耗时: 1.0042 秒

注意:这里 yield 没有返回值,所以 with timer() as xx 会是 None


🔍 示例 3:带返回值的资源

1@contextmanager
2def database_connection():
3    conn = connect_to_db()  # 假设这是连接数据库的函数
4    try:
5        yield conn
6    finally:
7        conn.close()

使用:

1with database_connection() as db:
2    db.execute("SELECT ...")

❗ 注意事项

  1. 必须 yield 一次且仅一次

    • 多次 yield 会导致 RuntimeError: generator didn't stop after throw()
    • yield 也会报错。
  2. 异常处理

    • 如果 with 块中抛出异常,异常会在 yield 处重新抛出。
    • 若你想捕获并处理异常(比如记录日志但不中断),可以在 except 中处理,但要小心不要吞掉异常(除非有意为之)。
    1@contextmanager
    2def ignore_exception():
    3    try:
    4        yield
    5    except Exception as e:
    6        print(f"忽略异常: {e}")
    7        # 不 re-raise,异常被吞掉
  3. 不能替代所有上下文管理器

    • 对于复杂状态机或需要多次进入/退出的场景,还是建议用类实现 __enter__/__exit__

💡 与类实现对比

类方式:

1class MyContext:
2    def __enter__(self):
3        print("进入")
4        return "资源"
5    def __exit__(self, exc_type, exc_val, exc_tb):
6        print("退出")

@contextmanager 方式:

1@contextmanager
2def my_context():
3    print("进入")
4    try:
5        yield "资源"
6    finally:
7        print("退出")

后者更轻量、易读,尤其适合简单场景。


✅ 总结

特点 说明
来源 from contextlib import contextmanager
本质 把生成器函数转为上下文管理器
结构 setup → yield → cleanup(用 try/finally 包裹)
优点 代码简洁、避免写类、逻辑集中
适用场景 资源获取/释放、临时状态修改(如切换目录、修改环境变量)、计时、锁等

如果你经常写 with 语句相关的工具函数,@contextmanager 是提升代码优雅度的利器!

@property

基本概念

  • 属性装饰器:@property 是 Python 内置装饰器,用于将方法转换为只读属性
  • 访问方式:可以通过 instance.property_name 方式访问,无需括号

主要用途

  1. 只读属性实现:
    • 提供受控的属性访问接口
    • 可在访问时执行验证、计算或处理逻辑
  2. 封装性:
    • 隐藏内部实现细节
    • 统一属性访问接口
  3. 动态计算:
    • 延迟计算属性值
    • 根据对象当前状态动态返回结果
@property
def llm_metadata(self) -> dict[str, Any]:
    """Metadata to be passed to the LLM when using this condenser."""
    if not self._llm_metadata:
        logger.warning(
            'LLM metadata is empty. Ensure to set it in the condenser implementation.'
        )
    return self._llm_metadata

这样可以通过 condenser.llm_metadata 直接访问,而不需要调用 condenser.llm_metadata(),同时还能在访问时进行空值检查和日志记录。

类的成员变量和@property的主要区别

  1. 动态计算 vs 静态值:
    • @property 可以在每次访问时动态计算返回值
    • 直接定义的类属性是静态的,初始化后值就固定了
  2. 附加逻辑处理:
    • @property 可以在访问时执行额外逻辑(如验证、日志记录)
    • 直接属性无法在访问时执行任何处理
  3. 封装性:
    • @property 隐藏了内部实现细节(如 self._llm_metadata)
    • 直接属性暴露了实现细节

实际好处

  • 运行时验证:每次访问都能检查 _llm_metadata 是否为空
  • 条件处理:可以根据当前状态决定返回什么值
  • 接口一致性:外部代码无需关心这是计算属性还是存储属性
  • 未来扩展性:可以轻松添加更多处理逻辑而不改变接口

这种方式提供了比直接属性更强大的功能和更好的封装性。

@property.setter

基本概念

  • 属性设置器:@property.setter 是 Python 中用于定义属性设置方法的装饰器
  • 配对使用:必须与对应的 @property 装饰器配合使用

主要功能

  1. 控制属性赋值:
    • 允许在设置属性值时执行自定义逻辑
    • 提供数据验证和类型检查能力
  2. 保持接口一致性:
    • 外部代码仍然可以使用 instance.property = value 的简单语法
    • 内部实现可以包含复杂的处理逻辑
@timestamp.setter
def timestamp(self, value: datetime) -> None:
    if isinstance(value, datetime):
        self._timestamp = value.isoformat()

@property
def llm_metrics(self) -> Metrics | None:
    if hasattr(self, '_llm_metrics'):
        metrics = getattr(self, '_llm_metrics')
        return metrics if isinstance(metrics, Metrics) else None
    return None

@llm_metrics.setter
def llm_metrics(self, value: Metrics) -> None:
    self._llm_metrics = value

使用优势

  • 数据验证:可以在赋值时检查数据的有效性
  • 类型转换:自动进行必要的类型转换(如 datetime 转 isoformat 字符串)
  • 封装性:隐藏内部存储细节(使用 _timestamp 而非 timestamp)
  • 向后兼容:可以在不改变外部接口的情况下修改内部实现

这种方式使得属性既有只读属性的安全性,又有灵活的赋值控制能力。

@overload

基本概念

  • 函数重载装饰器:@overload 是 Python typing 模块提供的装饰器,用于实现函数或方法的重载
  • 类型提示专用:仅用于静态类型检查,不影响运行时行为

基本规则

  • 只需要声明签名:使用 @overload 装饰器的函数只需要声明函数签名,不需要具体实现
  • 占位符语法:通常使用 ... 作为函数体,表示这是一个占位符

工作机制

  • 纯声明作用:@overload 装饰器的函数仅用于类型检查器识别不同的函数签名
  • 实际实现在最后:真正的函数实现在最后一个未使用 @overload 装饰的函数中
  • 运行时无影响:@overload 函数在运行时会被忽略,不会执行

主要用途

  1. 多重签名支持:
    • 允许同一个方法有多种不同的参数类型组合
    • 为每种参数组合提供精确的返回类型提示
  2. 静态类型检查:
    • 帮助类型检查工具(如 MyPy)更好地理解代码
    • 提供更准确的 IDE 自动补全和错误检测
@overload
def __getitem__(self, key: slice) -> list[Event]: ...

@overload
def __getitem__(self, key: int) -> Event: ...

def __getitem__(self, key: int | slice) -> Event | list[Event]:
    # 这里才是真正的实现
    if isinstance(key, slice):
        # 实现逻辑...
    elif isinstance(key, int):
        # 实现逻辑...

工作原理

  1. 声明多个签名:使用多个 @overload 装饰器声明不同的函数签名
  2. 实现统一方法:最后提供一个实际实现,处理所有重载情况
  3. 类型检查:类型检查器会根据调用时的参数类型匹配相应签名

优势

  • 类型安全性:提供精确的类型信息,避免类型错误
  • API 清晰度:明确表明方法支持的不同调用方式
  • 开发体验:改善 IDE 提示和静态分析效果

这种方式让 View 类的 getitem 方法既能处理索引访问(返回单个 Event),也能处理切片访问(返回 list[Event]),同时保持类型检查的准确性。

使用 @overload 的主要原因

  1. 类型检查精度:

    # 有了 @overload,类型检查器能准确知道:
    view[0]        # 返回 Event 类型
    view[0:2]      # 返回 list[Event] 类型
    
    # 没有 @overload,类型检查器只能推断:
    view[0]        # 返回 Event | list[Event]
    view[0:2]      # 返回 Event | list[Event]
  2. IDE 支持增强:

    • 提供更准确的自动补全
    • 更好的错误检测
    • 更精确的参数提示
  3. 代码文档化:

    • 明确表达了方法支持的不同调用方式
    • 提高代码可读性和维护性

总结 虽然不使用 @overload 功能上也能工作,但使用它可以显著提升类型安全性和开发体验,特别是在大型项目中这种精确的类型信息非常有价值。

关键字

yield

yield 是一个关键字,用于定义 生成器函数(generator function)。它的核心作用是:让函数在每次调用时“暂停”并返回一个值,下次调用时从暂停处继续执行,而不是像普通函数那样一次性执行完并返回。

🔑 核心概念

  1. 普通函数 vs 生成器函数
  • 普通函数使用 return,执行完就结束。
  • 生成器函数使用 yield,可以多次“产出”值,并保持状态。
# 普通函数
def normal_func():
    return 1
    return 2  # 这行永远不会执行

# 生成器函数
def gen_func():
    yield 1
    yield 2
    yield 3

调用生成器函数不会立即执行代码,而是返回一个 生成器对象(generator object)

g = gen_func()
print(g)  # <generator object gen_func at 0x...>
  1. 如何获取 yield 的值?

通过 next()for 循环:

g = gen_func()
print(next(g))  # 1
print(next(g))  # 2
print(next(g))  # 3
# print(next(g))  # 抛出 StopIteration 异常

或更常见的:

for value in gen_func():
    print(value)
# 输出:1 2 3

yield 的主要优势

  1. 节省内存(惰性求值)

生成器不会一次性生成所有数据,而是在需要时才计算下一个值,非常适合处理大数据或无限序列。

# 普通函数:生成 100 万个数字 → 占用大量内存
def get_numbers():
    return list(range(1000000))

# 生成器:按需生成 → 内存占用极小
def gen_numbers():
    for i in range(1000000):
        yield i
  1. 支持无限序列
def count_up():
    n = 0
    while True:
        yield n
        n += 1

# 使用(注意:这是无限循环,需加 break)
for i in count_up():
    if i > 5:
        break
    print(i)  # 0 1 2 3 4 5
  1. 简化迭代器实现

不用手动写 __iter____next__

🧩 高级用法:yield 表达式(可接收值)

yield 不仅能产出值,还能接收外部传入的值(通过 .send()):

def echo():
    while True:
        received = yield
        print(f"收到: {received}")

g = echo()
next(g)          # 启动生成器(停在 yield 处)
g.send("Hello")  # 输出:收到: Hello
g.send("World")  # 输出:收到: World

这种机制可用于协程(coroutine),是 asyncio 等异步编程的基础。

📌 小结:yield 的作用

特性 说明
暂停与恢复 函数执行到 yield 暂停,下次调用从下一行继续
返回值 yield 后的表达式作为本次迭代的值
内存高效 惰性求值,适合大数据流
创建迭代器 自动实现迭代协议(__iter__, __next__
支持双向通信 通过 .send() 与生成器交互

💡 一句话理解:

yield 让函数变成一个“可暂停、可恢复、按需产粮”的工厂,而不是一次性把所有产品堆在仓库里。

如果你有具体使用场景(比如读文件、爬虫、协程等),我可以给出更针对性的例子!

魔术方法(Magic Methods)

什么是魔术方法?

  • 魔术方法是 Python 中以双下划线开头和结尾的特殊方法(如 init, str, getitem)
  • 它们允许开发者自定义类的行为,使自定义对象能够像内置类型一样工作
  • 魔术方法通常不需要直接调用,而是在特定操作时被 Python 自动调用

常见的魔术方法及用途

  • init: 初始化对象,当创建类实例时自动调用
  • str: 定义对象的字符串表示,用于 print() 和 str() 函数
  • len: 定义对象的长度,用于 len() 函数
  • getitem: 允许对象支持索引和切片操作,如 obj[key]
  • __setitem__: 允许通过索引赋值,如 obj[key] = value
  • iter: 使对象可迭代,可用于 for 循环
  • call: 使对象实例可以像函数一样被调用

如何使用魔术方法?

  1. 在类定义中实现相应的魔术方法
  2. Python 会在适当的时候自动调用这些方法
  3. 例如在 View 类中:
    • 实现了 len 方法,所以可以直接使用 len(view_instance)
    • 实现了 iter 方法,所以可以用 for event in view_instance
    • 实现了 getitem 方法,所以可以使用 view_instance[0] 或 view_instance[1:3]

这种方式让自定义类具有与内置类型相似的行为和语义

class View(BaseModel):
    events: list[Event]
	
    def __len__(self) -> int:
        return len(self.events)
    
    def __iter__(self):
        return iter(self.events)
    
    @overload
    def __getitem__(self, key: slice) -> list[Event]: ...

    @overload
    def __getitem__(self, key: int) -> Event: ...

    def __getitem__(self, key: int | slice) -> Event | list[Event]:
        if isinstance(key, slice):
            start, stop, step = key.indices(len(self))
            return [self[i] for i in range(start, stop, step)]
        elif isinstance(key, int):
            return self.events[key]
        else:
            raise ValueError(f'Invalid key type: {type(key)}')

这个 __getitem__ 函数是 Python 的魔术方法,允许你像访问列表一样通过索引或切片来访问 View 对象中的事件。 具体调用方式如下:

  1. 通过整数索引获取单个事件:

    first_event = view_instance[0]  # 获取第一个事件
    last_event = view_instance[-1]  # 获取最后一个事件
  2. 这种情况下,方法返回一个单独的 Event 对象。

    some_events = view_instance[1:4]   # 获取索引为 1, 2, 3 的事件列表
    first_five = view_instance[:5]     # 获取前五个事件的列表
    from_index_two = view_instance[2:] # 获取从索引 2 开始到末尾的所有事件列表
    all_events_copy = view_instance[:] # 获取所有事件的一个副本列表

这种情况下,方法返回一个包含 Event 对象的列表。 当你在代码中使用 view_instance[key] 这样的语法时,Python 会自动调用 view_instance.__getitem__(key) 方法。你通常不需要直接显式地调用 getitem 方法本身。

__str__

  • 字符串表示: str 方法定义了对象的"非正式"字符串表示形式,用于生成易于阅读的文本描述
  • 自动调用: 当使用 str() 函数、print() 函数或字符串格式化操作对象时,会自动调用该方法
  • 用户友好: 与 repr 不同,str 更注重提供对用户友好的、可读性强的字符串输出

内置类型

object

str

strip()

主要功能:

  • 去除空白字符:移除字符串开头和结尾的空白字符(包括空格、制表符、换行符等)
  • 返回新字符串:不修改原字符串,而是返回一个新的处理后的字符串
text = "  hello world  "
cleaned = text.strip()  # 结果: "hello world"

# 也可以指定要移除的字符
text = "***hello***"
cleaned = text.strip("*")  # 结果: "hello"

枚举

from enum import Enum

class EventSource(str, Enum):
    AGENT = 'agent'
    USER = 'user'
    ENVIRONMENT = 'environment'


    
class Color(Enum):
	RED = 1
    BLUE = 2
    GREEN = 3

# 使用
# - attribute access:
>>> Color.RED
<Color.RED: 1>

# - value lookup:
>>> Color(1)
<Color.RED: 1>

# - name lookup:
>>> Color['RED']
<Color.RED: 1>

ClassVar

ClassVar 是 Python typing 模块中的一个特殊构造函数,用于类型提示。 主要用途:

  • 类变量声明:表明一个变量属于类本身而不是类的实例
  • 类型检查器指导:帮助 mypy 等静态类型检查器理解变量是类变量而非实例变量
class Action(Event):
	runnable: ClassVar[bool] = False

这行代码声明了: runnable 是一个类变量(不是实例变量)

  • 类型提示为 bool
  • 默认值为 False
  • 在所有 Action 类的实例间共享

关键区别:

  • ClassVar:在所有实例间共享,属于类本身
  • 普通实例变量:每个实例拥有独立副本

使用优势:

  • 内存效率:无论创建多少实例,只存在一份拷贝
  • 意图明确:清楚地表明变量是类范围的
  • 类型安全:提供更好的类型检查和 IDE 支持
  • 这种模式常用于需要在所有类实例间共享的常量或配置值。

ClassVar 与直接定义类变量的区别和优势 主要区别

  1. 语义明确性
    • 直接定义: runnable = False - 无法明确区分是类变量还是实例变量
    • 使用 ClassVar: runnable: ClassVar[bool] = False - 明确表明这是类变量
  2. 类型检查支持
    • 直接定义: 类型检查器可能无法准确识别变量的作用域
    • 使用 ClassVar: 为 mypy 等类型检查工具提供明确信息

核心优势

  • 防止误用: 明确指示该变量属于类级别,不应在实例层面修改
  • 更好的 IDE 支持: 提供更准确的代码补全和重构建议
  • 代码可读性: 使代码意图更加清晰,便于其他开发者理解
  • 静态分析: 帮助工具更好地分析和验证代码结构

实际应用场景 在 Action 类中使用 ClassVar 明确表示 runnable 属性是所有动作实例共享的类级别配置,而不是每个实例独有的属性。这有助于维护代码的一致性和可维护性。

内置函数

isinstance

判断某一个对象是不是某一个类的实体对象

isinstance(实例, 类)

判断多个类型

isinstance(action, (
    AgentDelegateAction,
    AgentThinkAction,
    IPythonRunCellAction,
    FileEditAction,
    FileReadAction,
    BrowseInteractiveAction,
    BrowseURLAction,
    MCPAction,
    TaskTrackingAction,
))

enumerate

用于将一个可迭代对象(如列表、元组等)组合成一个索引序列,同时返回索引和对应的元素值。

主要功能:

  • 添加索引: 为可迭代对象的每个元素添加从0开始的索引
  • 返回元组: 返回包含索引和元素值的元组 (index, value)
  • 简化循环: 在需要同时获取索引和值的循环中非常有用
# 需要index
for i, event in enumerate(events):

# 不需要index
for event in events:

这段代码的作用是:

  • 同时获取 events 列表中每个元素的索引 i 和元素值 event
  • 索引 i 用于 current_index 参数,传递给 _process_observation 方法
  • 这样可以在处理每个事件时知道它在列表中的位置

使用优势:

  • 简洁性: 避免手动维护计数器变量
  • 可读性: 代码更清晰易懂
  • 效率: 内置函数,性能优化

reversed

list反转

iter

all

all 是 Python 内置函数,用于检查可迭代对象中的所有元素是否都为 True。

功能特点:

  • 返回值:
    • 如果所有元素都为真(或可迭代对象为空),返回 True
    • 如果有任何元素为假,立即返回 False(短路求值)
  • 工作原理:
    • 对可迭代对象中的每个元素应用布尔转换
    • 一旦遇到第一个假值就停止检查并返回 False
    • 只有当所有元素都为真时才返回 True
# 所有元素为真
all([1, 2, 3])  # True

# 存在假值
all([1, 0, 3])  # False

# 空列表
all([])  # True

# 字符串检查
all(['a', 'b', 'c'])  # True
all(['a', '', 'c'])   # False

any

功能:

  • any 是 Python 内置函数,用于检查可迭代对象中是否至少有一个元素为 True

返回值:

  • 如果至少有一个元素为真,返回 True
  • 如果所有元素都为假或可迭代对象为空,返回 False

短路求值:

  • 一旦找到第一个真值就立即返回,不会继续检查剩余元素