本文讲一下langchain.load.serializable.py里面的Serializable,这个在langchain的schema里面有被用到,比如docu,emt.py里面。 具体代码: ```python from abc import ABC from typing import Any, Dict, List, Literal, TypedDict, Union, cast from pydantic import BaseModel, PrivateAttr class BaseSerialized(TypedDict): """Base class for serialized objects.""" lc: int id: List[str] class SerializedConstructor(BaseSerialized): """Serialized constructor.""" type: Literal["constructor"] kwargs: Dict[str, Any] class SerializedSecret(BaseSerialized): """Serialized secret.""" type: Literal["secret"] class SerializedNotImplemented(BaseSerialized): """Serialized not implemented.""" type: Literal["not_implemented"] class Serializable(BaseModel, ABC): """Serializable base class.""" @property def lc_serializable(self) -> bool: """ Return whether or not the class is serializable. """ return False @property def lc_namespace(self) -> List[str]: """ Return the namespace of the langchain object. eg. ["langchain", "llms", "openai"] """ return self.__class__.__module__.split(".") @property def lc_secrets(self) -> Dict[str, str]: """ Return a map of constructor argument names to secret ids. eg. {"openai_api_key": "OPENAI_API_KEY"} """ return dict() @property def lc_attributes(self) -> Dict: """ Return a list of attribute names that should be included in the serialized kwargs. These attributes must be accepted by the constructor. """ return {} class Config: extra = "ignore" _lc_kwargs = PrivateAttr(default_factory=dict) def __init__(self, **kwargs: Any) -> None: super().__init__(**kwargs) self._lc_kwargs = kwargs def to_json(self) -> Union[SerializedConstructor, SerializedNotImplemented]: if not self.lc_serializable: return self.to_json_not_implemented() secrets = dict() # Get latest values for kwargs if there is an attribute with same name lc_kwargs = { k: getattr(self, k, v) for k, v in self._lc_kwargs.items() if not (self.__exclude_fields__ or {}).get(k, False) # type: ignore } # Merge the lc_secrets and lc_attributes from every class in the MRO for cls in [None, *self.__class__.mro()]: # Once we get to Serializable, we're done if cls is Serializable: break # Get a reference to self bound to each class in the MRO this = cast(Serializable, self if cls is None else super(cls, self)) print(this.lc_secrets) print(this.lc_attributes) secrets.update(this.lc_secrets) lc_kwargs.update(this.lc_attributes) # include all secrets, even if not specified in kwargs # as these secrets may be passed as an environment variable instead for key in secrets.keys(): secret_value = getattr(self, key, None) or lc_kwargs.get(key) if secret_value is not None: lc_kwargs.update({key: secret_value}) return { "lc": 1, "type": "constructor", "id": [*self.lc_namespace, self.__class__.__name__], "kwargs": lc_kwargs if not secrets else _replace_secrets(lc_kwargs, secrets), } def to_json_not_implemented(self) -> SerializedNotImplemented: return to_json_not_implemented(self) def _replace_secrets( root: Dict[Any, Any], secrets_map: Dict[str, str] ) -> Dict[Any, Any]: result = root.copy() for path, secret_id in secrets_map.items(): [*parts, last] = path.split(".") current = result for part in parts: if part not in current: break current[part] = current[part].copy() current = current[part] if last in current: current[last] = { "lc": 1, "type": "secret", "id": [secret_id], } return result def to_json_not_implemented(obj: object) -> SerializedNotImplemented: """Serialize a "not implemented" object. Args: obj: object to serialize Returns: SerializedNotImplemented """ _id: List[str] = [] try: if hasattr(obj, "__name__"): _id = [*obj.__module__.split("."), obj.__name__] elif hasattr(obj, "__class__"): _id = [*obj.__class__.__module__.split("."), obj.__class__.__name__] except Exception: pass return { "lc": 1, "type": "not_implemented", "id": _id, } ``` 先了解一些小东西:主要是typing中的一些数据类型 - Any:是一个特殊的类型注解,表示可以是任何类型。使用 Any 类型注解的变量可以接受任何类型的值,但是这种方式会失去静态类型检查的优势,因此应该尽量避免使用。 - TypeDcit:在 Python 3.8 中,引入了 typing 模块中的 TypedDict 类型注解。TypedDict 可以被用来描述键值对的字典类型,该类型中的键需要指定名称和类型注解,而值的类型可以是任意的。 - Literal: 是一个泛型类型注解,用于表示只能取特定值中的一个的字面量类型。例如 Literal[True, False] 表示只能取 True 或 False 中的一个值。 - Union:是一个泛型类型注解,用于表示多个类型中的任意一个。可以将多个类型作为 Union 的参数传入,例如 Union[str, int] 表示可以是字符串类型或整型类型中的任意一个。 - cast: 是 Python 中的一个内置函数,用于将一个值强制转换成指定的类型。常见的用法是在类型注解中使用, - `Optional`:是 `typing` 模块中的一个泛型类型,用于表示可选类型。它用于指示一个变量或函数参数可以是指定的类型,也可以是 `None`。 接下来看上面的代码: 这段代码定义了一个 `Serializable` 类,它是基于 `pydantic.BaseModel` 和 `abc.ABC` 类构建的序列化基类,可以用于将对象序列化为 JSON 格式的字典。 该类包含以下几个子类: 1. `BaseSerialized`:用于定义序列化对象的基本结构,包含 `lc` 和 `id` 两个键,分别表示语言链版本和对象的唯一标识符列表。 2. `SerializedConstructor`:用于表示一个构造函数的序列化对象,除了 `lc` 和 `id`,还包含一个键 `type`,表示对象的类型是构造函数,以及一个键 `kwargs`,表示构造函数的所有参数及其对应的值。 3. `SerializedSecret`:用于表示一个加密的序列化对象,除了 `lc` 和 `id`,还包含一个键 `type`,表示对象的类型是加密的。 4. `SerializedNotImplemented`:用于表示一个未实现的序列化对象,除了 `lc` 和 `id`,还包含一个键 `type`,表示对象的类型是未实现的。 该类包含以下几个属性和方法: 1. `lc_serializable`:表示该对象是否可序列化的布尔值,默认为 `False`。 2. `lc_namespace`:表示对象的命名空间,以列表形式返回,默认为该类的模块名和类名。 3. `lc_secrets`:表示对象的构造函数参数与加密密钥之间的映射关系,以字典形式返回,默认为空字典。 4. `lc_attributes`:表示对象的属性列表,以字典形式返回,默认为空字典。 5. `Config`:用于配置 `pydantic.BaseModel` 的行为,默认为忽略额外的字段。 6. `_lc_kwargs`:表示对象的构造函数参数和对应的值,是一个私有属性,默认值为空字典。 7. `__init__`:重载 `pydantic.BaseModel` 的构造函数,用于初始化 `_lc_kwargs` 属性。 8. `to_json`:将对象序列化为 JSON 格式的字典。该方法首先检查 `lc_serializable` 属性的值,如果为 `False`,则返回 `SerializedNotImplemented` 对象;否则,获取所有类的 `lc_secrets` 和 `lc_attributes` 属性,合并成一个字典,并将 `_lc_kwargs` 和加密密钥映射关系合并到该字典中,最后返回 `SerializedConstructor` 对象。 9. `to_json_not_implemented`:将对象序列化为 `SerializedNotImplemented` 对象。 此外,该代码还定义了一个 `_replace_secrets` 函数,用于将字典中的加密密钥替换为加密的 JSON 对象。 使用上述代码的一个例子: ```python from typing import Optional from pydantic import SecretStr class Person(Serializable): name: str age: int secret_key: Optional[SecretStr] = None @property def lc_serializable(self) -> bool: return True @property def lc_secrets(self) -> Dict[str, str]: return {"secret_key": self.secret_key} @property def lc_attributes(self) -> Dict: return dict(self) person = Person(name="Alice", age=30, secret_key="abc") person_json = person.to_json() print(person_json) """ {'lc': 1, 'type': 'constructor', 'id': ['__main__', 'Person'], 'kwargs': {'name': 'Alice', 'age': 30, 'secret_key': {'lc': 1, 'type': 'secret', 'id': [SecretStr('**********')]}}} """ ``` 到这里,我们已经基本理解了这个类的作用了。