找回密码
 立即注册
搜索
热搜: 活动 交友
查看: 2410|回复: 0

Pydantic | 一个用于 Python 的数据合法性验证库

[复制链接]

15

主题

30

回帖

1083

积分

版主

积分
1083
发表于 3-25-2025 21:07:08 | 显示全部楼层 |阅读模式
在实际项目开发中,我们经常会遇到配置数据格式混乱、数据类型不匹配的问题。例如,考虑下面这样一个 JSON 配置文件:
{
  "program_display_language": "zh_CN",
  "log_level": "DEBUG",
  "platform": "bilibili",
  "room_id": "1852504554",
  "danmaku_display_mode": "BOTTOM",
  "sending_delay": 2,
  "segment_time_length": 5,
  "model_name": "turbo",
  "whisper_params": {
    "language": "en",
    "fp16": true,
    "suppress_tokens": [
      -1
    ],
    "temperature": 0,
    "beam_size": 5,
    "best_of": 5
  },
  "translator": "ChatGPT",
  "translator_params": {
    "dst_language": "简体中文",
    "temperature": 0
  },
  "max_order_num": 10,
  "danmaku_text_format": "{transcription_text} {danmaku_order_num}",
  "should_send_danmaku": false,
  "max_retry_times": 3,
  "max_chars_per_danmaku": 20,
  "max_chars_per_audio_segment": 40,
  "debug": false
}
在这个例子中,如果 "max_order_num" 被错误地输入了非数字字符,或者 "platform" 的值被意外设置成了 "炒饭",那么无疑会给数据合法性校验带来不少麻烦。为了避免在项目中编写冗长而复杂的检查代码,Pydantic 便应运而生——它利用 Python 的类型提示机制,提供了一种优雅而高效的数据验证方案。
什么是 Pydantic?
Pydantic 是一个基于 Python 类型提示的高性能数据解析和验证库。它通过继承自 BaseModel 的模型类,允许开发者以声明性方式定义数据结构及其类型约束。借助 Pydantic,你可以确保在数据输入阶段就捕捉到潜在的错误,从而使整个应用程序更健壮、更易维护。
例如,我们可以针对上述 JSON 配置文件定义如下的数据模型:
from pydantic import BaseModel
from typing import Literal

class TranslatorParams(BaseModel):
    model_name: str
    api_key: str

class Config(BaseModel):
    program_display_language: Literal["zh_CN", "en"]
    log_level: Literal["DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL"]
    platform: str
    room_id: int
    danmaku_display_mode: Literal["FLY", "TOP", "BOTTOM"]
    sending_delay: int
    segment_time_length: int
    model_name: Literal["tiny", "base", "small", "medium", "large", "turbo"]
    whisper_params: dict
    translator: Literal["ChatGPT"]
    translator_params: TranslatorParams
    danmaku_text_format: str
    max_order_num: int
    should_send_danmaku: bool
    max_retry_times: int
    max_chars_per_danmaku: int
    max_chars_per_audio_segment: int
    debug: bool

class Credential(BaseModel):  # 假设 MyBaseModel 为 BaseModel 的扩展
    SESSDATA: str
    bili_jct: str

if __name__ == '__main__':
    path = "./config.json"
    with open(path) as f:
        json_data = f.read()
        config = Config.model_validate_json(json_data)
        print(config)
上述代码中,Pydantic 根据我们定义的类型注解自动将 JSON 数据转换为相应的 Python 类型,并在数据不符合预期时抛出友好的验证错误。
Python 类型提示简介
掌握 Pydantic 的关键在于理解 Python 的类型提示。类型提示不仅为代码提供了更好的可读性,还能借助静态类型检查工具(例如 PEP-484 中介绍的规范)提高开发效率。
为什么
在 Java、C# 等强类型语言中,变量和方法参数的类型是必不可少的。引入类型提示的目的是在不影响 Python 动态特性的前提下,为代码提供额外的类型信息,帮助 IDE 提供更智能的自动补全、静态代码检查以及重构支持。
基本用法
  • 简单变量声明:  
    count: int = 0
  • 复合类型:  
    from typing import Union, List
    value: Union[int, str] = 42
    numbers: List[int] = [1, 2, 3]
通过这种方式,开发者可以清晰地描述数据类型,从而使代码在开发和维护过程中更具可读性和可维护性。
Pydantic 的基本用法
Pydantic 的核心在于 BaseModel 类。通过继承 BaseModel,你可以定义一个数据模型,并利用 Python 的类型提示来描述每个字段的类型。当数据不符合预期时,Pydantic 会自动抛出 ValidationError,让你可以迅速定位问题。
例如:
from pydantic import BaseModel

class User(BaseModel):
    id: int
    name: str = "Anonymous"

user = User(id="123")  # 字符串 "123" 会自动转换为整数
print(user.dict())     # 输出: {'id': 123, 'name': 'Anonymous'}
在数据解析失败的情况下,Pydantic 会明确指出哪一个字段的数据不合法,从而大大简化调试工作。
进一步探索
Validators
在某些情况下,仅依赖类型提示可能不足以满足复杂的数据校验需求。例如,我们希望确保 room_id 是一个 正整数,而不是负数或零。此外,sending_delay 应该在 1 到 10 之间,否则会影响程序的运行逻辑。
在 Pydantic 中,我们可以使用 @field_validator(Pydantic v2)或 @validator(Pydantic v1)来自定义字段验证逻辑。以下是一个具体的示例:
from pydantic import BaseModel, field_validator, ValidationError

class Config(BaseModel):
    room_id: int
    sending_delay: int
   
    @field_validator("room_id")
    @classmethod
    def check_room_id(cls, value):
        """确保 room_id 是正整数"""
        if value <= 0:
            raise ValueError("room_id 必须是一个正整数")
        return value

    @field_validator("sending_delay")
    @classmethod
    def check_sending_delay(cls, value):
        """确保 sending_delay 在 1 到 10 之间"""
        if not (1 <= value <= 10):
            raise ValueError("sending_delay 必须在 1 到 10 之间")
        return value

# 测试
try:
    config = Config(room_id=-100, sending_delay=5)  # room_id 不合法
except ValidationError as e:
    print(e)

try:
    config = Config(room_id=123, sending_delay=15)  # sending_delay 不合法
except ValidationError as e:
    print(e)

try:
    config = Config(room_id=456, sending_delay=3)  # 合法数据
    print("验证成功:", config)
except ValidationError as e:
    print(e)
通过利用 Pydantic,你不仅可以避免繁琐的手写验证代码,还能借助 Python 类型提示带来的诸多好处,使你的项目更加健壮和易于维护。无论是在构建 API 接口、配置管理还是数据解析方面,Pydantic 都提供了一个既直观又高效的解决方案。
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

Archiver|手机版|小黑屋|RealDevClub ( 沪ICP备2024093864号-1 )

GMT+8, 2-20-2026 09:19 , Processed in 0.063618 second(s), 20 queries .

Powered by Discuz! X3.5

© 2001-2026 Discuz! Team.

快速回复 返回顶部 返回列表