如果你不想使用 Pydantic 的 BaseModel ,你也可以在标准的数据类(在 Python 3.7 中引入)上获得相同的数据验证。
from datetime import datetime
from pydantic.dataclasses import dataclass
@dataclass
class User:
    id: int
    name: str = 'John Doe'
    signup_ts: datetime = None
user = User(id='42', signup_ts='2032-06-21T12:00')
print(user)
"""
User(id=42, name='John Doe', signup_ts=datetime.datetime(2032, 6, 21, 12, 0))
"""
请注意, pydantic.dataclasses.dataclass 不能替代 pydantic.BaseModel 。 pydantic.dataclasses.dataclass 提供了类似于 dataclasses.dataclass 的功能,并添加了 Pydantic 验证。在某些情况下,子类化 pydantic.BaseModel 是更好的选择。
For more information and discussion see
[pydantic/pydantic#710](https://github.com/pydantic/pydantic/issues/710).
Pydantic 数据类和 BaseModel 之间的一些差异包括:
- 初始化钩子的工作原理
- JSON 转储
你可以使用所有标准的 Pydantic 字段类型。请注意,传递给构造函数的参数将被复制,以便执行验证和必要的强制转换。
要对 Pydantic 数据类执行验证或生成 JSON 模式,现在应该用  TypeAdapter  包装该数据类,并使用其方法。
需要 default_factory 的字段可以通过 pydantic.Field 或 dataclasses.field 指定。
import dataclasses
from typing import List, Optional
from pydantic import Field, TypeAdapter
from pydantic.dataclasses import dataclass
@dataclass
class User:
    id: int
    name: str = 'John Doe'
    friends: List[int] = dataclasses.field(default_factory=lambda: [0])
    age: Optional[int] = dataclasses.field(
        default=None,
        metadata=dict(title='The age of the user', description='do not lie!'),
    )
    height: Optional[int] = Field(None, title='The height in cm', ge=50, le=300)
user = User(id='42')
print(TypeAdapter(User).json_schema())
"""
{
    'properties': {
        'id': {'title': 'Id', 'type': 'integer'},
        'name': {'default': 'John Doe', 'title': 'Name', 'type': 'string'},
        'friends': {
            'items': {'type': 'integer'},
            'title': 'Friends',
            'type': 'array',
        },
        'age': {
            'anyOf': [{'type': 'integer'}, {'type': 'null'}],
            'default': None,
            'description': 'do not lie!',
            'title': 'The age of the user',
        },
        'height': {
            'anyOf': [
                {'maximum': 300, 'minimum': 50, 'type': 'integer'},
                {'type': 'null'},
            ],
            'default': None,
            'title': 'The height in cm',
        },
    },
    'required': ['id'],
    'title': 'User',
    'type': 'object',
}
"""
pydantic.dataclasses.dataclass 的参数与标准装饰器相同,只是多了一个额外的关键字参数 config ,其含义与 model_config 相同。
警告
在 v1.2 之后,必须安装 Mypy 插件才能对 pydantic 数据类进行类型检查。
有关将验证器与数据类结合使用的更多信息,请参见数据类验证器。
数据类配置¶
如果你想像修改 BaseModel 一样修改 config ,你有两个选择:
- 
将配置应用于数据类装饰器作为字典 
- 
使用 ConfigDict作为配置从 pydantic 导入 ConfigDict 从 pydantic.dataclasses 导入 dataclass 选项 1-直接使用字典¶注意:mypy仍会引发拼写错误¶@dataclass(config=dict(validate_assignment=True)) # (1)! 类 MyDataclass1:a:int 选项 2-使用ConfigDict¶(与运行时之前的情况相同,因为它是一个TypedDict,但具有智能感知)¶@dataclass(config=ConfigDict(validate_assignment=True)) 类 MyDataclass2:a:int 
- 
你可以在 API 参考文档中了解更多关于 validate_assignment的信息。
注意
Pydantic 数据类支持  extra  配置,可将 ignore 、 forbid 或 allow 传递给初始化器的额外字段。但是,stdlib 数据类的某些默认行为可能会占主导地位。例如,当使用 extra='allow' 的 Pydantic 数据类被初始化时,任何存在于该数据类上的额外字段都会被省略。
嵌套的数据类¶
嵌套的数据类在数据类和普通模型中都得到了支持。
from pydantic import AnyUrl
from pydantic.dataclasses import dataclass
@dataclass
class NavbarButton:
    href: AnyUrl
@dataclass
class Navbar:
    button: NavbarButton
navbar = Navbar(button={'href': 'https://example.com'})
print(navbar)
#> Navbar(button=NavbarButton(href=Url('https://example.com/')))
当用作字段时,数据类(Pydantic 或原始的)应该使用字典作为验证输入。
通用数据类¶
Pydantic 支持泛型数据类,包括具有类型变量的数据类。
from typing import Generic, TypeVar
from pydantic import TypeAdapter
from pydantic.dataclasses import dataclass
T = TypeVar('T')
@dataclass
class GenericDataclass(Generic[T]):
    x: T
validator = TypeAdapter(GenericDataclass)
assert validator.validate_python({'x': None}).x is None
assert validator.validate_python({'x': 1}).x == 1
assert validator.validate_python({'x': 'a'}).x == 'a'
请注意,如果您将数据类用作 BaseModel 的字段或通过 FastAPI,则不需要 TypeAdapter 。
Stdlib 数据类和 Pydantic 数据类¶
从 stdlib 中继承 dataclasses
标准库中的数据类(嵌套或不嵌套)也可以被继承,Pydantic 将自动验证所有继承的字段。
import dataclasses
import pydantic
@dataclasses.dataclass
class Z:
    z: int
@dataclasses.dataclass
class Y(Z):
    y: int = 0
@pydantic.dataclasses.dataclass
class X(Y):
    x: int = 0
foo = X(x=b'1', y='2', z='3')
print(foo)
#> X(z=3, y=2, x=1)
try:
    X(z='pika')
except pydantic.ValidationError as e:
    print(e)
    """
    1 validation error for X
    z
      Input should be a valid integer, unable to parse string as an integer [type=int_parsing, input_value='pika', input_type=str]
    """
使用带有 BaseModel 的 stdlib dataclasses
请记住,当与 BaseModel 混合时,标准库的 dataclasses(嵌套或不嵌套)会自动转换为 Pydantic 的 dataclasses。此外,生成的 Pydantic dataclass 将具有与原始 dataclass 完全相同的配置( order , frozen ,...)。
import dataclasses
from datetime import datetime
from typing import Optional
from pydantic import BaseModel, ConfigDict, ValidationError
@dataclasses.dataclass(frozen=True)
class User:
    name: str
@dataclasses.dataclass
class File:
    filename: str
    last_modification_time: Optional[datetime] = None
class Foo(BaseModel):
    # Required so that pydantic revalidates the model attributes
    model_config = ConfigDict(revalidate_instances='always')
    file: File
    user: Optional[User] = None
file = File(
    filename=['not', 'a', 'string'],
    last_modification_time='2020-01-01T00:00',
)  # nothing is validated as expected
print(file)
"""
File(filename=['not', 'a', 'string'], last_modification_time='2020-01-01T00:00')
"""
try:
    Foo(file=file)
except ValidationError as e:
    print(e)
    """
    1 validation error for Foo
    file.filename
      Input should be a valid string [type=string_type, input_value=['not', 'a', 'string'], input_type=list]
    """
foo = Foo(file=File(filename='myfile'), user=User(name='pika'))
try:
    foo.user.name = 'bulbi'
except dataclasses.FrozenInstanceError as e:
    print(e)
    #> cannot assign to field 'name'
使用自定义类型¶
由于标准库中的dataclasses会自动转换为添加验证,因此使用自定义类型可能会导致一些意外行为。在这种情况下,您可以在配置中简单地添加 arbitrary_types_allowed !
import dataclasses
from pydantic import BaseModel, ConfigDict
from pydantic.errors import PydanticSchemaGenerationError
class ArbitraryType:
    def __init__(self, value):
        self.value = value
    def __repr__(self):
        return f'ArbitraryType(value={self.value!r})'
@dataclasses.dataclass
class DC:
    a: ArbitraryType
    b: str
# valid as it is a builtin dataclass without validation
my_dc = DC(a=ArbitraryType(value=3), b='qwe')
try:
    class Model(BaseModel):
        dc: DC
        other: str
    # invalid as it is now a pydantic dataclass
    Model(dc=my_dc, other='other')
except PydanticSchemaGenerationError as e:
    print(e.message)
    """
    Unable to generate pydantic-core schema for <class '__main__.ArbitraryType'>. Set `arbitrary_types_allowed=True` in the model_config to ignore this error or implement `__get_pydantic_core_schema__` on your type to fully support it.
    If you got this error by calling handler(<some type>) within `__get_pydantic_core_schema__` then you likely need to call `handler.generate_schema(<some type>)` since we do not call `__get_pydantic_core_schema__` on `<some type>` otherwise to avoid infinite recursion.
    """
class Model(BaseModel):
    model_config = ConfigDict(arbitrary_types_allowed=True)
    dc: DC
    other: str
m = Model(dc=my_dc, other='other')
print(repr(m))
#> Model(dc=DC(a=ArbitraryType(value=3), b='qwe'), other='other')
检查一个数据类是否是 pydantic 数据类
Pydantic 数据类仍被视为数据类,因此使用 dataclasses.is_dataclass 将返回 True 。要检查一个类型是否特定为 Pydantic 数据类,可以使用 pydantic.dataclasses.is_pydantic_dataclass 。
import dataclasses
import pydantic
@dataclasses.dataclass
class StdLibDataclass:
    id: int
PydanticDataclass = pydantic.dataclasses.dataclass(StdLibDataclass)
print(dataclasses.is_dataclass(StdLibDataclass))
#> True
print(pydantic.dataclasses.is_pydantic_dataclass(StdLibDataclass))
#> False
print(dataclasses.is_dataclass(PydanticDataclass))
#> True
print(pydantic.dataclasses.is_pydantic_dataclass(PydanticDataclass))
#> True
初始化挂钩¶
当你初始化一个数据类时,可以借助 @model_validator 修饰符 mode 参数,在验证之前或之后执行代码。
from typing import Any, Dict
from typing_extensions import Self
from pydantic import model_validator
from pydantic.dataclasses import dataclass
@dataclass
class Birth:
    year: int
    month: int
    day: int
@dataclass
class User:
    birth: Birth
    @model_validator(mode='before')
    @classmethod
    def pre_root(cls, values: Dict[str, Any]) -> Dict[str, Any]:
        print(f'First: {values}')
        """
        First: ArgsKwargs((), {'birth': {'year': 1995, 'month': 3, 'day': 2}})
        """
        return values
    @model_validator(mode='after')
    def post_root(self) -> Self:
        print(f'Third: {self}')
        #> Third: User(birth=Birth(year=1995, month=3, day=2))
        return self
    def __post_init__(self):
        print(f'Second: {self.birth}')
        #> Second: Birth(year=1995, month=3, day=2)
user = User(**{'birth': {'year': 1995, 'month': 3, 'day': 2}})
在 Pydantic 数据类中, post_init 在验证器中间被调用。以下是顺序:
- 
model_validator(mode='before')
- 
field_validator(mode='before')
- 
field_validator(mode='after')
- 
内部验证器。例如,对 int、str等类型的验证。
- 
__post_init__.
- 
model_validator(mode='after')从 dataclasses导入InitVar,从pathlib导入Path,从typing导入Optional从 pydantic.dataclasses 导入 dataclass @dataclass 类 PathData: path: Path base_path: InitVar[Optional[Path]] def post_init(self, base_path): print(f'Received path={self.path!r}, base_path={base_path!r}') #> Received path='world', base_path='/hello' if base_path is not None: self.path = base_path / self.path path_data = PathData('world', base_path='/hello') 收到路径='世界',基础路径='/hello'¶断言 path_data.path 等于路径('/hello/world') 
与 stdlib dataclasses 的区别
请注意,Python 标准库中的 dataclasses.dataclass 仅实现了 post_init 方法,因为它没有运行验证步骤。
JSON 转储¶
Pydantic 数据类没有 .model_dump_json() 函数。要将它们转储为 JSON,您需要如下使用 RootModel:
import dataclasses
from typing import List
from pydantic import RootModel
from pydantic.dataclasses import dataclass
@dataclass
class User:
    id: int
    name: str = 'John Doe'
    friends: List[int] = dataclasses.field(default_factory=lambda: [0])
user = User(id='42')
print(RootModel[User](User(id='42')).model_dump_json(indent=4))
"""
{
    "id": 42,
    "name": "John Doe",
    "friends": [
        0
    ]
}
"""
本文总阅读量次