0

0

使用 Pydantic 实现延迟 ForwardRef 的方法

DDD

DDD

发布时间:2025-10-17 14:06:19

|

157人浏览过

|

来源于php中文网

原创

使用 pydantic 实现延迟 forwardref 的方法

本文旨在解决 Pydantic 中 `ForwardRef` 无法延迟加载的问题,通过介绍 discriminated unions 的使用方法,并提供代码示例,帮助开发者在处理包含子类的对象模型时,避免繁琐的隐藏机制,实现更简洁、灵活的代码结构,特别是在模块化开发中跨模块引用时。

在使用 Pydantic 构建复杂的数据模型时,经常会遇到类之间相互引用的情况,特别是当涉及到继承关系时。ForwardRef 可以用于解决循环依赖的问题,但有时我们需要延迟 ForwardRef 的解析,例如,当子类定义在其他模块中,且加载顺序无法保证时。本文将介绍一种使用 discriminated unions 的方法,避免手动隐藏类型变量,实现更优雅的延迟加载。

使用 Discriminated Unions

Discriminated Unions 是一种 Pydantic 特性,它允许根据某个字段的值来区分联合类型中的不同子类型。这可以有效地解决需要在运行时才能确定具体类型的问题。

以下是一个使用 discriminated unions 的示例,该示例定义了 Pet 基类和 Dog、Cat 子类,并使用 AnyPet 类型来表示它们的联合类型:

from pydantic import BaseModel, Field
from typing import Literal, Annotated, Union


class Pet(BaseModel):
    """Animal class"""
    name: str
    age: int


class Dog(Pet):
    """Dog class"""
    type: Literal["dog"] = "dog"
    breed: str


class Cat(Pet):
    """Cat class"""
    type: Literal["cat"] = "cat"
    breed: str


AnyPet = Annotated[Union[Dog, Cat], Field(discriminator="type")]


class Home(BaseModel):
    """Home class"""
    pet: AnyPet


data = {
    "pet": {
        "type": "dog",
        "name": "Buddy",
        "age": 4,
        "breed": "Golden Retriever"
    }
}

home = Home(**data)
print(home)

在这个例子中,AnyPet 使用 Annotated 和 Union 定义,Field(discriminator="type") 指定了使用 type 字段来区分 Dog 和 Cat 类型。当 Pydantic 解析 Home 模型时,会根据 pet 字典中的 type 字段的值来确定具体的 Pet 子类型。

跨模块场景下的应用

如果你的模型分布在多个模块中,并且无法轻易地生成所有子类的列表,可以考虑以下几种方法:

  1. 集中管理子类定义: 尽量将所有相关的子类定义放在同一个模块或子模块中,并在 __init__.py 中定义 AnyPet 类型。这样可以确保所有子类在定义 AnyPet 之前都被加载。

    白月生产企业订单管理系统GBK2.0  Build 080807
    白月生产企业订单管理系统GBK2.0 Build 080807

    请注意以下说明:1、本程序允许任何人免费使用。2、本程序采用PHP+MYSQL架构编写。并且经过ZEND加密,所以运行环境需要有ZEND引擎支持。3、需要售后服务的,请与本作者联系,联系方式见下方。4、本程序还可以与您的网站想整合,可以实现用户在线服务功能,可以让客户管理自己的信息,可以查询自己的订单状况。以及返点信息等相关客户利益的信息。这个功能可提高客户的向心度。安装方法:1、解压本系统,放在

    下载
    pets/
    ├── __init__.py  # 定义 AnyPet
    ├── cats.py      # 定义 Cat
    └── dogs.py      # 定义 Dog
  2. 自动生成联合类型: 如果无法集中管理子类,可以编写代码自动检测基类的所有子类,并生成联合类型。

    from pydantic import BaseModel
    from typing import Union, Annotated, Field
    
    class Pet(BaseModel):
        name: str
        age: int
    
    # 假设 Dog 和 Cat 在其他模块中定义并导入
    from .dogs import Dog
    from .cats import Cat
    
    valid_sub_classes = []
    
    for sub_class in Pet.__subclasses__():
        field = sub_class.model_fields.get("type", None)
    
        if field is None:
            raise ValueError(f"{sub_class.__name__} is missing a 'type' field")
    
        valid_sub_classes.append(sub_class)
    
    AnyPet = Annotated[Union[tuple(valid_sub_classes)], Field(discriminator="type")]
    print(AnyPet)

    这种方法可以动态地发现所有子类,并将其合并到联合类型中。

  3. 延迟执行: 如果上述方法都不可行,可以定义一个函数来延迟执行生成联合类型的代码。

    from pydantic import BaseModel
    from typing import Union, Annotated, Field
    
    def get_any_pet():
        class Pet(BaseModel):
            name: str
            age: int
    
        # 假设 Dog 和 Cat 在其他模块中定义并导入
        from .dogs import Dog
        from .cats import Cat
    
        valid_sub_classes = []
    
        for sub_class in Pet.__subclasses__():
            field = sub_class.model_fields.get("type", None)
    
            if field is None:
                raise ValueError(f"{sub_class.__name__} is missing a 'type' field")
    
            valid_sub_classes.append(sub_class)
    
        return Annotated[Union[tuple(valid_sub_classes)], Field(discriminator="type")]
    
    AnyPet = get_any_pet()
    
    class Home(BaseModel):
        pet: AnyPet

    这种方法可以将类型定义推迟到运行时,确保所有子类都已加载。

注意事项

  • 确保所有子类都定义了一个用于区分类型的字段,并在 Field(discriminator="type") 中正确指定。
  • 在跨模块场景下,仔细考虑模块的加载顺序,并选择合适的解决方案。
  • 如果使用自动生成联合类型的方法,确保基类的所有子类都符合预期。

总结

通过使用 discriminated unions,可以有效地解决 Pydantic 中 ForwardRef 无法延迟加载的问题,并避免繁琐的隐藏机制。在跨模块场景下,可以结合集中管理子类定义、自动生成联合类型或延迟执行等方法,实现更灵活、可维护的代码结构。选择哪种方法取决于具体的项目结构和需求。

相关专题

更多
c语言union的用法
c语言union的用法

c语言union的用法是一种特殊的数据类型,它允许在相同的内存位置存储不同的数据类型,union的使用可以帮助我们节省内存空间,并且可以方便地在不同的数据类型之间进行转换。使用union时需要注意对应的成员是有效的,并且只能同时访问一个成员。本专题为大家提供union相关的文章、下载、课程内容,供大家免费下载体验。

125

2023.09.27

Java JVM 原理与性能调优实战
Java JVM 原理与性能调优实战

本专题系统讲解 Java 虚拟机(JVM)的核心工作原理与性能调优方法,包括 JVM 内存结构、对象创建与回收流程、垃圾回收器(Serial、CMS、G1、ZGC)对比分析、常见内存泄漏与性能瓶颈排查,以及 JVM 参数调优与监控工具(jstat、jmap、jvisualvm)的实战使用。通过真实案例,帮助学习者掌握 Java 应用在生产环境中的性能分析与优化能力。

19

2026.01.20

PS使用蒙版相关教程
PS使用蒙版相关教程

本专题整合了ps使用蒙版相关教程,阅读专题下面的文章了解更多详细内容。

61

2026.01.19

java用途介绍
java用途介绍

本专题整合了java用途功能相关介绍,阅读专题下面的文章了解更多详细内容。

87

2026.01.19

java输出数组相关教程
java输出数组相关教程

本专题整合了java输出数组相关教程,阅读专题下面的文章了解更多详细内容。

39

2026.01.19

java接口相关教程
java接口相关教程

本专题整合了java接口相关内容,阅读专题下面的文章了解更多详细内容。

10

2026.01.19

xml格式相关教程
xml格式相关教程

本专题整合了xml格式相关教程汇总,阅读专题下面的文章了解更多详细内容。

13

2026.01.19

PHP WebSocket 实时通信开发
PHP WebSocket 实时通信开发

本专题系统讲解 PHP 在实时通信与长连接场景中的应用实践,涵盖 WebSocket 协议原理、服务端连接管理、消息推送机制、心跳检测、断线重连以及与前端的实时交互实现。通过聊天系统、实时通知等案例,帮助开发者掌握 使用 PHP 构建实时通信与推送服务的完整开发流程,适用于即时消息与高互动性应用场景。

19

2026.01.19

微信聊天记录删除恢复导出教程汇总
微信聊天记录删除恢复导出教程汇总

本专题整合了微信聊天记录相关教程大全,阅读专题下面的文章了解更多详细内容。

160

2026.01.18

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
Go 教程
Go 教程

共32课时 | 4万人学习

Go语言实战之 GraphQL
Go语言实战之 GraphQL

共10课时 | 0.8万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号