ABC 是 Python 标准库 abc 模块中的一个类,全称是 "Abstract Base Class"(抽象基类)。 主要作用:
- 定义抽象接口:ABC 用于创建不能被直接实例化的抽象类
- 强制子类实现:通过 @abstractmethod 装饰器标记的方法必须在子类中被实现
- 供模板:为子类提供通用的接口规范和部分实现
class Condenser(ABC):
@abstractmethod
def condense(self, View) -> View | Condensation:
"""这个方法必须在子类中实现"""
pass优势:
- 接口一致性:确保所有 Condenser 的子类都实现相同的接口
- 设计规范:强制开发者遵循预定义的架构模式
- 类型安全:提供更好的代码提示和静态检查支持
通过使用 ABC,Condenser 类成为一个抽象基类,定义了所有具体 condenser 实现必须遵循的接口规范。
基本概念
- 抽象方法标记:@abstractmethod 是 Python abc 模块提供的装饰器,用于标记抽象方法
- 强制实现:被标记的方法必须在子类中被实现,否则会抛出 TypeError
@abstractmethod
def condense(self, View) -> View | Condensation:
"""Condense a sequence of events into a potentially smaller list."""主要特点
- 接口规范:
- 定义了所有 Condenser 子类必须实现的标准接口
- 确保不同的压缩策略都提供相同的调用方式
- 设计约束:
- 强制子类实现具体的压缩逻辑
- 提供统一的返回类型 (View | Condensation)
- 文档说明:
- 明确告知开发者如何实现自己的压缩策略
- 指出可以调用 self.add_metadata 记录诊断信息
实际效果
- 如果某个类继承了 Condenser 但没有实现 condense 方法,实例化时会报错
- 确保了多态性,不同压缩器可以以相同方式调用 condense 方法
这种方式提供了良好的架构设计,既保证了接口一致性,又允许灵活的具体实现。
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__方法主要区别
- 数据验证
- 传统方式:需要手动编写验证逻辑
- BaseModel:基于类型提示自动验证
- 序列化支持
- 传统方式:需要手动实现 to_dict() 等方法
- BaseModel:自带 model_dump(), model_dump_json() 方法
- 代码简洁性
- 传统方式:需要大量样板代码
- BaseModel:声明式定义,代码更简洁
- 错误处理
- 传统方式:需要自己处理各种边界情况
- BaseModel:提供详细的验证错误信息
在 View 类中使用 BaseModel 可以确保 events 字段始终是有效的 Event 列表,且 unhandled_condensation_request 始终是布尔值,同时获得了便捷的序列化能力。
覆盖类默认的系列化规则,可以自定义序列化规则输出。
可以序列化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'}主要区别
- 控制级别:
- plain: 对输出格式拥有完全控制权
- wrap: 扩展或修改现有行为
- 复杂度:
- plain: 实现更简单但需要承担更多责任
- wrap: 实现更复杂但可利用默认行为
- 灵活性:
- plain: 最适合完全自定义序列化需求
- wrap: 最适合增强标准序列化功能
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 函数,开发者可以精确控制每个配置字段的行为和默认值,同时保持代码的清晰性和可维护性。
主要功能
- 自动生成特殊方法:自动为类生成 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 的优势
- 数据验证:
- BaseModel 支持运行时类型检查和数据验证
- 可以定义复杂的验证规则
- 序列化支持:
- 内置 model_dump(), model_json() 等方法
- 支持 JSON 序列化和反序列化
- 嵌套模型:
- 更好地处理嵌套数据结构
- 支持复杂的数据模型关系
- 字段控制:
- 更灵活的字段配置选项
- 支持别名、排除、只读等属性
- ORM 集成:
- 更容易与数据库模型集成
- 支持更多企业级应用特性
总的来说,BaseModel 比 @dataclass 更强大,特别适合构建复杂的、需要数据验证的应用程序。但在简单场景下,@dataclass 可能更轻量级。
field 是 Python dataclasses 模块提供的一个函数,用于指定 dataclass 类中字段的特殊配置。
field 的作用:
- 配置字段行为:允许为 dataclass 字段设置默认值、工厂函数等特殊属性
- 避免可变默认值问题:通过 default_factory 参数安全地创建可变类型的默认值
这里 field(default_factory=dict) 的含义:
- 为 extra_data 字段设置了一个工厂函数 dict
- 每次创建新实例时,都会调用 dict() 来生成一个新的空字典作为默认值
- 避免了直接使用 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=...) 而直接赋默认值,会出现共享问题:
- dict: 字典类型,所有实例会共享同一个字典对象
- list: 列表类型,所有实例会共享同一个列表对象
- set: 集合类型,所有实例会共享同一个集合对象
- 自定义可变对象: 任何用户自定义的可变类实例,如果直接作为默认值,也会被所有实例共享
# 错误的做法 - 会导致共享问题
@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是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"主要特点
- 第一个参数是类本身:类方法的第一个参数是cls,代表类本身,不是实例。
- 可以通过类或实例调用:类方法既可以通过类名直接调用,也可以通过实例调用。
- 无法访问实例变量:类方法不能直接访问实例变量(即self.xxx形式的变量)
基本概念
- 静态方法装饰器:@staticmethod 用于定义类中的静态方法
- 无需实例访问:静态方法不接收 self 或 cls 参数,无法访问类或实例的属性
主要特点
- 独立性:
- 不依赖类实例或类本身的状态
- 可以通过类名直接调用,无需创建实例
- 功能定位:
- 通常用于与类相关但不需访问类数据的工具函数
- 逻辑上属于类的命名空间,但功能相对独立
@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 这样的工厂方法,它创建并返回类实例,但不需要访问现有实例或类的状态。
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 x 中 x 会是 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 ...")❗ 注意事项
-
必须
yield一次且仅一次- 多次
yield会导致RuntimeError: generator didn't stop after throw()。 - 不
yield也会报错。
- 多次
-
异常处理
- 如果
with块中抛出异常,异常会在yield处重新抛出。 - 若你想捕获并处理异常(比如记录日志但不中断),可以在
except中处理,但要小心不要吞掉异常(除非有意为之)。
1@contextmanager 2def ignore_exception(): 3 try: 4 yield 5 except Exception as e: 6 print(f"忽略异常: {e}") 7 # 不 re-raise,异常被吞掉
- 如果
-
不能替代所有上下文管理器
- 对于复杂状态机或需要多次进入/退出的场景,还是建议用类实现
__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 是 Python 内置装饰器,用于将方法转换为只读属性
- 访问方式:可以通过 instance.property_name 方式访问,无需括号
主要用途
- 只读属性实现:
- 提供受控的属性访问接口
- 可在访问时执行验证、计算或处理逻辑
- 封装性:
- 隐藏内部实现细节
- 统一属性访问接口
- 动态计算:
- 延迟计算属性值
- 根据对象当前状态动态返回结果
@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的主要区别
- 动态计算 vs 静态值:
- @property 可以在每次访问时
动态计算返回值 - 直接定义的类属性是静态的,初始化后值就固定了
- @property 可以在每次访问时
- 附加逻辑处理:
- @property 可以在访问时
执行额外逻辑(如验证、日志记录) - 直接属性无法在访问时执行任何处理
- @property 可以在访问时
- 封装性:
- @property 隐藏了内部实现细节(如 self._llm_metadata)
- 直接属性暴露了实现细节
实际好处
- 运行时验证:每次访问都能检查 _llm_metadata 是否为空
- 条件处理:可以根据当前状态决定返回什么值
- 接口一致性:外部代码无需关心这是计算属性还是存储属性
- 未来扩展性:可以轻松添加更多处理逻辑而不改变接口
这种方式提供了比直接属性更强大的功能和更好的封装性。
基本概念
- 属性设置器:@property.setter 是 Python 中用于定义属性设置方法的装饰器
- 配对使用:必须与对应的 @property 装饰器配合使用
主要功能
- 控制属性赋值:
- 允许在设置属性值时执行自定义逻辑
- 提供数据验证和类型检查能力
- 保持接口一致性:
- 外部代码仍然可以使用 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 是 Python typing 模块提供的装饰器,用于实现函数或方法的重载
- 类型提示专用:仅用于静态类型检查,不影响运行时行为
基本规则
- 只需要声明签名:使用 @overload 装饰器的函数只需要声明函数签名,
不需要具体实现 - 占位符语法:通常使用 ... 作为函数体,表示这是一个占位符
工作机制
- 纯声明作用:@overload 装饰器的函数仅用于类型检查器识别不同的函数签名
- 实际实现在最后:真正的函数实现在最后一个未使用 @overload 装饰的函数中
- 运行时无影响:@overload 函数在运行时会被忽略,不会执行
主要用途
- 多重签名支持:
- 允许同一个方法有多种不同的参数类型组合
- 为每种参数组合提供精确的返回类型提示
- 静态类型检查:
- 帮助类型检查工具(如 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):
# 实现逻辑...工作原理
- 声明多个签名:使用多个 @overload 装饰器声明不同的函数签名
- 实现统一方法:最后提供一个实际实现,处理所有重载情况
- 类型检查:类型检查器会根据调用时的参数类型匹配相应签名
优势
- 类型安全性:提供精确的类型信息,避免类型错误
- API 清晰度:明确表明方法支持的不同调用方式
- 开发体验:改善 IDE 提示和静态分析效果
这种方式让 View 类的 getitem 方法既能处理索引访问(返回单个 Event),也能处理切片访问(返回 list[Event]),同时保持类型检查的准确性。
使用 @overload 的主要原因
-
类型检查精度:
# 有了 @overload,类型检查器能准确知道: view[0] # 返回 Event 类型 view[0:2] # 返回 list[Event] 类型 # 没有 @overload,类型检查器只能推断: view[0] # 返回 Event | list[Event] view[0:2] # 返回 Event | list[Event]
-
IDE 支持增强:
- 提供更准确的自动补全
- 更好的错误检测
- 更精确的参数提示
-
代码文档化:
- 明确表达了方法支持的不同调用方式
- 提高代码可读性和维护性
总结 虽然不使用 @overload 功能上也能工作,但使用它可以显著提升类型安全性和开发体验,特别是在大型项目中这种精确的类型信息非常有价值。
yield 是一个关键字,用于定义 生成器函数(generator function)。它的核心作用是:让函数在每次调用时“暂停”并返回一个值,下次调用时从暂停处继续执行,而不是像普通函数那样一次性执行完并返回。
🔑 核心概念
- 普通函数 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...>- 如何获取 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 的主要优势
- 节省内存(惰性求值)
生成器不会一次性生成所有数据,而是在需要时才计算下一个值,非常适合处理大数据或无限序列。
# 普通函数:生成 100 万个数字 → 占用大量内存
def get_numbers():
return list(range(1000000))
# 生成器:按需生成 → 内存占用极小
def gen_numbers():
for i in range(1000000):
yield i- 支持无限序列
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- 简化迭代器实现
不用手动写 __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让函数变成一个“可暂停、可恢复、按需产粮”的工厂,而不是一次性把所有产品堆在仓库里。
如果你有具体使用场景(比如读文件、爬虫、协程等),我可以给出更针对性的例子!
什么是魔术方法?
- 魔术方法是 Python 中以双下划线开头和结尾的特殊方法(如 init, str, getitem)
- 它们允许开发者自定义类的行为,使自定义对象能够像内置类型一样工作
- 魔术方法通常不需要直接调用,而是在特定操作时被 Python 自动调用
常见的魔术方法及用途
- init: 初始化对象,当创建类实例时自动调用
- str: 定义对象的字符串表示,用于 print() 和 str() 函数
- len: 定义对象的长度,用于 len() 函数
- getitem: 允许对象支持索引和切片操作,如 obj[key]
__setitem__: 允许通过索引赋值,如 obj[key] = value- iter: 使对象可迭代,可用于 for 循环
- call: 使对象实例可以像函数一样被调用
如何使用魔术方法?
- 在类定义中实现相应的魔术方法
- Python 会在适当的时候自动调用这些方法
- 例如在 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 对象中的事件。
具体调用方式如下:
-
通过整数索引获取单个事件:
first_event = view_instance[0] # 获取第一个事件 last_event = view_instance[-1] # 获取最后一个事件
-
这种情况下,方法返回一个单独的 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() 函数、print() 函数或字符串格式化操作对象时,会自动调用该方法
- 用户友好: 与 repr 不同,str 更注重提供对用户友好的、可读性强的字符串输出
主要功能:
- 去除空白字符:移除字符串开头和结尾的空白字符(包括空格、制表符、换行符等)
- 返回新字符串:不修改原字符串,而是返回一个新的处理后的字符串
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 是 Python typing 模块中的一个特殊构造函数,用于类型提示。 主要用途:
- 类变量声明:表明一个变量属于类本身而不是类的实例
- 类型检查器指导:帮助 mypy 等静态类型检查器理解变量是类变量而非实例变量
class Action(Event):
runnable: ClassVar[bool] = False这行代码声明了: runnable 是一个类变量(不是实例变量)
- 类型提示为 bool
- 默认值为 False
- 在所有 Action 类的实例间共享
关键区别:
- ClassVar:在所有实例间共享,属于类本身
- 普通实例变量:每个实例拥有独立副本
使用优势:
- 内存效率:无论创建多少实例,只存在一份拷贝
- 意图明确:清楚地表明变量是类范围的
- 类型安全:提供更好的类型检查和 IDE 支持
- 这种模式常用于需要在所有类实例间共享的常量或配置值。
ClassVar 与直接定义类变量的区别和优势 主要区别
- 语义明确性
- 直接定义: runnable = False - 无法明确区分是类变量还是实例变量
- 使用 ClassVar: runnable: ClassVar[bool] = False - 明确表明这是类变量
- 类型检查支持
- 直接定义: 类型检查器可能无法准确识别变量的作用域
- 使用 ClassVar: 为 mypy 等类型检查工具提供明确信息
核心优势
- 防止误用: 明确指示该变量属于类级别,不应在实例层面修改
- 更好的 IDE 支持: 提供更准确的代码补全和重构建议
- 代码可读性: 使代码意图更加清晰,便于其他开发者理解
- 静态分析: 帮助工具更好地分析和验证代码结构
实际应用场景 在 Action 类中使用 ClassVar 明确表示 runnable 属性是所有动作实例共享的类级别配置,而不是每个实例独有的属性。这有助于维护代码的一致性和可维护性。
判断某一个对象是不是某一个类的实体对象
isinstance(实例, 类)
判断多个类型
isinstance(action, (
AgentDelegateAction,
AgentThinkAction,
IPythonRunCellAction,
FileEditAction,
FileReadAction,
BrowseInteractiveAction,
BrowseURLAction,
MCPAction,
TaskTrackingAction,
))用于将一个可迭代对象(如列表、元组等)组合成一个索引序列,同时返回索引和对应的元素值。
主要功能:
- 添加索引: 为可迭代对象的每个元素添加从0开始的索引
- 返回元组: 返回包含索引和元素值的元组 (index, value)
- 简化循环: 在需要同时获取索引和值的循环中非常有用
# 需要index
for i, event in enumerate(events):
# 不需要index
for event in events:这段代码的作用是:
- 同时获取 events 列表中每个元素的索引 i 和元素值 event
- 索引 i 用于 current_index 参数,传递给 _process_observation 方法
- 这样可以在处理每个事件时知道它在列表中的位置
使用优势:
- 简洁性: 避免手动维护计数器变量
- 可读性: 代码更清晰易懂
- 效率: 内置函数,性能优化
list反转
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 是 Python 内置函数,用于检查可迭代对象中是否至少有一个元素为 True
返回值:
- 如果至少有一个元素为真,返回 True
- 如果所有元素都为假或可迭代对象为空,返回 False
短路求值:
- 一旦找到第一个真值就立即返回,不会继续检查剩余元素