Pydantic 非默认参数在默认参数后面

5 投票
2 回答
93 浏览
提问于 2025-04-14 17:36

我不明白为什么这段代码:

from typing import Optional
from pydantic import Field
from pydantic.dataclasses import dataclass

@dataclass
class Klass:
    field1: str = Field(min_length=1)
    field2: str = Field(min_length=1)
    field3: Optional[str]

会报错:

TypeError: non-default argument 'field3' follows default argument

如果默认情况下,Fielddefault 参数是 PydanticUndefined。那为什么 field1field2 是默认参数呢?

我用的是 Python 3.8 和 Pydantic 2.6。

我试过 field3: Optional[str] = Field(...),这样是可以的。我原本以为上面的代码块也能正常工作,因为所有字段都是必填的,并且没有默认值。

2 个回答

1

这不是关于pydantic的问题,而是关于Python数据类的错误。

要修复这个错误,你只需要把field3放在field1field2的上面。

这个问题发生的原因是,在数据类运行时,它们会像普通类一样尝试初始化,就像__init__方法那样。数据类会一个一个地获取它的字段。我们知道,位置参数不能在关键字参数后面设置。在这里也是同样的情况。

3

简而言之

  • field3 是一个必填参数,类型是 Optional[str],并不是可选参数,因为在类定义中你没有给 field3 赋值。

  • field1field2 从技术上讲是可选的,因为你给它们赋的 Field 对象提供了一个默认值 PydanticUndefined。不过,如果你没有提供其他参数,这个值会在运行时导致验证错误。


dataclass 装饰器正在构建一个 def 语句,用来定义你类的 __init__ 方法,样子大概是这样的:

def __init__(self, field1=PydanticUndefined, field2=PydanticUndefined, field3):
    ...

构建好的语句会被 exec 执行,这就是为什么在定义类的时候会出现关于非默认参数的错误,而不是在你尝试实例化类的时候。

要让 field3 成为可选的,你需要提供一个默认值。

field3: Optional[str] = None

这样定义的语句就变成了:

def __init__(self, field1=PydanticUndefined, field2=PydanticUndefined, field3=None):
   ...

据我所知,你不能让 field1field2 真正变成必填项;PydanticUndefined 的值只是让 __init__ 抛出一个 ValidationError,而不是 TypeError,如果没有传入明确的参数。

>>> Klass()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/Users/chepner/py311/lib/python3.11/site-packages/pydantic/_internal/_dataclasses.py", line 134, in __init__
    s.__pydantic_validator__.validate_python(ArgsKwargs(args, kwargs), self_instance=s)
pydantic_core._pydantic_core.ValidationError: 2 validation errors for Klass
field1
  Field required [type=missing, input_value=ArgsKwargs(()), input_type=ArgsKwargs]
    For further information visit https://errors.pydantic.dev/2.5/v/missing
field2
  Field required [type=missing, input_value=ArgsKwargs(()), input_type=ArgsKwargs]
    For further information visit https://errors.pydantic.dev/2.5/v/missing

我还没有深入源代码去查看具体是怎么回事,但我猜大概是这样的:

def __init__(self, field1=PydanticUndefined, ...):
    if field1 is PydanticUndefined:
        # prepare ValidationError exception
    if field2 is PydanticUndefined:
        # prepare ValidationError exception
    if <ValidationError needs to be raised>:
        raise ValidationError(...)

如果需要的话,你可以通过在 Field 中添加 default 关键字参数,为 field1field2 提供“真实”的默认值。

撰写回答