Skip to content

性能优化技巧

在大多数情况下,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])
    # 使用 adapter 进行某些操作
from pydantic import TypeAdapter

adapter = TypeAdapter(list[int])

def my_func():
    ...
    # 使用 adapter 进行某些操作

Sequence 对比 listtupleMapping 对比 dict

使用 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

使用标记联合(tagged union),而不是普通联合

标记联合(或可区分联合)是一种带有指示类型的字段的联合类型。

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 的信息请参阅此处