性能提示¶
在大多数情况下,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
Sequence
与 list
或 tuple
,Mapping
与 dict
¶
当使用 Sequence
时,Pydantic 调用 isinstance(value, Sequence)
来检查值是否为序列。此外,Pydantic 将尝试针对不同类型的序列(如 list
和 tuple
)进行验证。如果您知道该值是 list
或 tuple
,请使用 list
或 tuple
而不是 Sequence
。
Mapping
和 dict
也是如此。如果您知道该值是 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
的更多信息:此处。