You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Pydantic 1.10:Field选项不足时,如何用@validate_arguments添加自定义验证

Pydantic自定义验证的实现方式

1. @validate_arguments的恰当用法

用@validate_arguments装饰器给函数添加参数验证是符合Python风格的做法,它能直接结合类型提示和Pydantic的验证能力,让代码简洁直观。

基础用法

直接在函数上装饰,结合类型提示实现简单验证:

from pydantic import validate_arguments, ValidationError

@validate_arguments
def calculate_area(radius: float):
    if radius <= 0:
        raise ValueError("半径必须大于0")
    return 3.14 * radius ** 2

try:
    calculate_area(-5)
except ValidationError as e:
    print(e)

结合Annotated与Field

如果需要更清晰的规则声明,可通过Annotated搭配Field把验证规则和类型注解绑定:

from pydantic import validate_arguments, Field
from typing import Annotated

@validate_arguments
def calculate_area(radius: Annotated[float, Field(gt=0, description="半径必须大于0")]):
    return 3.14 * radius ** 2

2. Field规则不足时的处理

当Field的内置选项(如gt、lt、regex等)无法满足需求时,有两种常用方案:

函数内直接添加验证逻辑

适合简单的一次性验证,直接在函数内部抛出异常:

@validate_arguments
def calculate_area(radius: float):
    if radius <= 0:
        raise ValueError("半径必须大于0")
    if radius > 100:
        raise ValueError("半径不能超过100")
    return 3.14 * radius ** 2

用AfterValidator抽离验证逻辑

如果验证逻辑需要复用,可将其封装成函数,通过AfterValidator注入到类型注解中:

from pydantic import validate_arguments, AfterValidator
from typing import Annotated

def check_radius(r: float) -> float:
    if r <= 0:
        raise ValueError("半径必须大于0")
    if r > 100:
        raise ValueError("半径不能超过100")
    return r

@validate_arguments
def calculate_area(radius: Annotated[float, AfterValidator(check_radius)]):
    return 3.14 * radius ** 2

3. 扩展Pydantic类型并重写验证逻辑

可以通过自定义类型扩展Pydantic的验证能力,这种方式推荐在验证逻辑需要多处复用的场景,避免重复代码。

自定义类型示例

from pydantic import GetCoreSchemaHandler
from pydantic_core import core_schema
from typing import Any

class PositiveLimitedFloat(float):
    @classmethod
    def __get_pydantic_core_schema__(cls, source_type: Any, handler: GetCoreSchemaHandler) -> core_schema.CoreSchema:
        # 复用float类型的基础验证,再附加自定义逻辑
        return core_schema.no_info_wrap_validator_function(
            cls.validate,
            handler(float),
        )
    
    @classmethod
    def validate(cls, v: Any) -> float:
        if not isinstance(v, (int, float)):
            raise TypeError("必须传入数字类型")
        v = float(v)
        if v <= 0:
            raise ValueError("数值必须大于0")
        if v > 100:
            raise ValueError("数值不能超过100")
        return v

# 直接使用自定义类型
@validate_arguments
def calculate_area(radius: PositiveLimitedFloat):
    return 3.14 * radius ** 2

如果只是单个场景使用,这种方式会有点过度设计,优先选择更轻量的验证方案即可。

4. 其他实现方式

用Pydantic模型封装参数

当函数参数较多、验证规则复杂时,可将参数封装为Pydantic模型,通过field_validator定义验证逻辑:

from pydantic import BaseModel, field_validator

class AreaRequest(BaseModel):
    radius: float
    
    @field_validator("radius")
    def check_radius(cls, v):
        if v <= 0:
            raise ValueError("半径必须大于0")
        if v > 100:
            raise ValueError("半径不能超过100")
        return v

def calculate_area(req: AreaRequest):
    return 3.14 * req.radius ** 2

# 调用时直接传入模型实例或字典
calculate_area(AreaRequest(radius=5))
calculate_area({"radius": 10})

结合BeforeValidator/PlainValidator

  • BeforeValidator:用于在类型转换前预处理数据(如字符串转数字)
  • PlainValidator:用于简单的无上下文验证逻辑

示例:

from pydantic import validate_arguments, BeforeValidator, PlainValidator
from typing import Annotated

def str_to_float(v: Any) -> float:
    if isinstance(v, str):
        return float(v.strip())
    return v

@validate_arguments
def calculate_area(radius: Annotated[float, BeforeValidator(str_to_float), PlainValidator(lambda x: x > 0)]):
    return 3.14 * radius ** 2

# 支持传入字符串格式的数字
calculate_area("10")

内容的提问来源于stack exchange,提问作者coolio

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.16 22:52:52