在查看LangChain源码时,可以看到这样一个类型注解:
- class Document(BaseMedia):
- page_content: str
- type: Literal["Document"] = "Document"
复制代码
这里的Literal是Python typing模块中的一个特殊类型注解,用于限定变量只能取值为指定的一组字面量。在Document类中,type字段被声明为Literal["Document"],意味着该字段只能被赋值为字符串"Document",任何其他值在静态代码检查时都会触发类型错误。
静态类型检查是在不实际运行程序的情况下,通过分析源代码文本结构来发现潜在错误的手段。例如在IDE或mypy中,如果尝试将type设置为"Image",就会立刻报错。
Literal在LangChain框架中有更深层的用途——序列化时的“防伪标签”。LangChain将Document对象转换成JSON时,type字段固定为"Document",确保反序列化时能正确识别对象类型。如下所示:
- # 转换后的JSON结构
- {
- "lc": 1,
- "type": "constructor",
- "id": ["langchain", "schema", "document", "Document"],
- "kwargs": {
- "metadata": {...},
- "page_content": "...",
- "type": "Document"
- }
- }
复制代码
除了静态检查,Literal还可以配合Pydantic实现运行时的数据校验。Pydantic是一个数据验证库,当字段被声明为Literal["red"]且默认值为"red"时,如果运行时试图赋值为"Image",Pydantic会抛出ValidationError,防止脏数据污染系统。示例:
- from pydantic import BaseModel
- from typing import Literal
- class MyClass(BaseModel):
- type: Literal["red"] = "red"
- obj = MyClass()
- obj.type = "Image" # ❌ Pydantic 抛出 ValidationError
复制代码
从这个例子可以看出,Literal注解在静态层面的限制与Pydantic运行时的校验形成了双重保障:开发阶段由IDE或mypy拦截错误,生产阶段由Pydantic确保数据的一致性。这种模式在需要严格枚举值的场景(如状态机、指令调度、序列化协议)中非常实用。
使用Literal时需要注意,它要求Python 3.8及以上版本(低版本需从typing_extensions导入)。其参数必须是不可变类型(字符串、数字、布尔值、字节串或枚举),不支持动态计算的值,否则会引发TypeError。 |