性能提示¶
在大多数情况下,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
```
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)
避免通过原始类型的子类添加额外信息¶
不要这样做
```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
的更多信息,请点击这里。
本文总阅读量次