跳转至

性能提示

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

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

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

在某些情况下, model_validate(json.loads(...)) 可能会更快。具体来说,当在模型上使用 'before''wrap' 验证器时,两步法可能会使验证更快。你可以在这个讨论中了解这些特殊情况的更多信息。

许多性能改进目前正在进行中,如这里所讨论的。一旦这些更改合并,我们应该达到 model_validate_json() 总是比 model_validate(json.loads(...)) 更快的地步。

TypeAdapter 实例化一次

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

:x:不好

```py
from typing import List

from pydantic import TypeAdapter


def my_func():
    adapter = TypeAdapter(List[int])
    # do something with adapter
```

“好”

```py
from typing import List

from pydantic import TypeAdapter

adapter = TypeAdapter(List[int])

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

Sequencelisttuple - Mappingdict

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

避免通过原始类型的子类添加额外信息

不要这样做

```py
class CompletedStr(str):
    def __init__(self, s: str):
        self.s = s
        self.done = False
```

这样做

```py
from pydantic import BaseModel


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

使用标记联合,而不是联合

标记联合(或已区分联合)是一种联合,其中有一个字段指示它是哪种类型。

from typing import Any

from typing_extensions import 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 List

from typing_extensions 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 的更多信息,请点击这里


本文总阅读量