跳到内容

性能提示

在大多数情况下,Pydantic 不会成为您的瓶颈,只有在您确定必要时才遵循此建议。

一般来说,使用 model_validate_json() 而不是 model_validate(json.loads(...))

model_validate(json.loads(...)) 中,JSON 在 Python 中解析,然后转换为字典,然后在内部进行验证。另一方面,model_validate_json() 已经在内部执行验证。

在少数情况下,model_validate(json.loads(...)) 可能会更快。特别是,当在模型上使用 'before''wrap' 验证器时,使用两步方法进行验证可能会更快。您可以在 此讨论 中阅读有关这些特殊情况的更多信息。

目前正在为 pydantic-core 进行许多性能改进,如 此处 讨论的那样。一旦这些更改合并,我们应该达到 model_validate_json() 始终比 model_validate(json.loads(...)) 更快的程度。

TypeAdapter 仅实例化一次

这里的想法是避免不必要地构建验证器和序列化器。每次实例化 TypeAdapter 时,它都会构建一个新的验证器和序列化器。如果您在函数中使用 TypeAdapter,则每次调用该函数时都会实例化它。相反,请实例化一次并重复使用它。

from pydantic import TypeAdapter


def my_func():
    adapter = TypeAdapter(list[int])
    # do something with adapter
from pydantic import TypeAdapter

adapter = TypeAdapter(list[int])

def my_func():
    ...
    # do something with adapter

SequencelisttupleMappingdict

当使用 Sequence 时,Pydantic 调用 isinstance(value, Sequence) 来检查值是否为序列。此外,Pydantic 将尝试针对不同类型的序列(如 listtuple)进行验证。如果您知道该值是 listtuple,请使用 listtuple 而不是 Sequence

Mappingdict 也是如此。如果您知道该值是 dict,请使用 dict 而不是 Mapping

在不需要验证时不要进行验证,使用 Any 来保持值不变

如果您不需要验证值,请使用 Any 来保持值不变。

from typing import Any

from pydantic import BaseModel


class Model(BaseModel):
    a: Any


model = Model(a=1)

避免通过原始类型的子类获取额外信息

class CompletedStr(str):
    def __init__(self, s: str):
        self.s = s
        self.done = False
from pydantic import BaseModel


class CompletedModel(BaseModel):
    s: str
    done: bool = False

使用标签联合类型,而不是联合类型

标签联合类型(或可区分联合类型)是一个联合类型,其中包含一个字段,指示它是哪种类型。

from typing import Any, Literal

from pydantic import BaseModel, Field


class DivModel(BaseModel):
    el_type: Literal['div'] = 'div'
    class_name: str | None = None
    children: list[Any] | None = None


class SpanModel(BaseModel):
    el_type: Literal['span'] = 'span'
    class_name: str | None = None
    contents: str | None = None


class ButtonModel(BaseModel):
    el_type: Literal['button'] = 'button'
    class_name: str | None = None
    contents: str | None = None


class InputModel(BaseModel):
    el_type: Literal['input'] = 'input'
    class_name: str | None = None
    value: str | None = None


class Html(BaseModel):
    contents: DivModel | SpanModel | ButtonModel | InputModel = Field(
        discriminator='el_type'
    )

有关更多详细信息,请参阅 可区分联合类型

使用 TypedDict 而不是嵌套模型

不要使用嵌套模型,而是使用 TypedDict 来定义数据结构。

性能比较

通过简单的基准测试,TypedDict 比嵌套模型快约 2.5 倍

from timeit import timeit

from typing_extensions import TypedDict

from pydantic import BaseModel, TypeAdapter


class A(TypedDict):
    a: str
    b: int


class TypedModel(TypedDict):
    a: A


class B(BaseModel):
    a: str
    b: int


class Model(BaseModel):
    b: B


ta = TypeAdapter(TypedModel)
result1 = timeit(
    lambda: ta.validate_python({'a': {'a': 'a', 'b': 2}}), number=10000
)
result2 = timeit(
    lambda: Model.model_validate({'b': {'a': 'a', 'b': 2}}), number=10000
)
print(result2 / result1)

如果真的关心性能,请避免包装验证器

包装验证器通常比其他验证器慢。这是因为它们要求在验证期间在 Python 中实现数据。包装验证器对于复杂的验证逻辑非常有用,但如果您正在寻求最佳性能,则应避免使用它们。

使用 FailFast 尽早失败

从 v2.8+ 开始,您可以将 FailFast 注解应用于序列类型,以便在序列中的任何项目验证失败时尽早失败。如果您使用此注解,如果一个项目失败,您将不会获得序列中其余项目的验证错误,因此您实际上是在牺牲可见性来换取性能。

from typing import Annotated

from pydantic import FailFast, TypeAdapter, ValidationError

ta = TypeAdapter(Annotated[list[bool], FailFast()])
try:
    ta.validate_python([True, 'invalid', False, 'also invalid'])
except ValidationError as exc:
    print(exc)
    """
    1 validation error for list[bool]
    1
      Input should be a valid boolean, unable to interpret input [type=bool_parsing, input_value='invalid', input_type=str]
    """

在此处阅读有关 FailFast 的更多信息:此处