Pydantic 非默认参数在默认参数后面
我不明白为什么这段代码:
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
如果默认情况下,Field
的 default
参数是 PydanticUndefined
。那为什么 field1
和 field2
是默认参数呢?
我用的是 Python 3.8 和 Pydantic 2.6。
我试过 field3: Optional[str] = Field(...)
,这样是可以的。我原本以为上面的代码块也能正常工作,因为所有字段都是必填的,并且没有默认值。
2 个回答
这不是关于pydantic的问题,而是关于Python数据类的错误。
要修复这个错误,你只需要把field3
放在field1
和field2
的上面。
这个问题发生的原因是,在数据类运行时,它们会像普通类一样尝试初始化,就像__init__
方法那样。数据类会一个一个地获取它的字段。我们知道,位置参数不能在关键字参数后面设置。在这里也是同样的情况。
简而言之
field3
是一个必填参数,类型是Optional[str]
,并不是可选参数,因为在类定义中你没有给field3
赋值。field1
和field2
从技术上讲是可选的,因为你给它们赋的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):
...
据我所知,你不能让 field1
或 field2
真正变成必填项;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
关键字参数,为 field1
和 field2
提供“真实”的默认值。