Советы по производительности¶
В большинстве случаев Pydantic не станет вашим узким местом, следуйте этому правилу только в том случае, если вы уверены, что это необходимо.
В общем, используйте model_validate_json()
а не model_validate(json.loads(...))
¶
На model_validate(json.loads(...))
JSON анализируется в Python, затем преобразуется в dict, а затем проверяется внутри. С другой стороны, model_validate_json()
уже выполняет внутреннюю проверку.
Есть несколько случаев, когда model_validate(json.loads(...))
может быть быстрее. В частности, при использовании валидатора 'before'
или 'wrap'
в модели проверка может быть быстрее с помощью двухэтапного метода. Вы можете прочитать больше об этих особых случаях в этом обсуждении .
Многие улучшения производительности в настоящее время находятся в разработке для pydantic-core
, как обсуждается здесь . Как только эти изменения будут объединены, мы должны оказаться в точке, где model_validate_json()
всегда быстрее, чем model_validate(json.loads(...))
.
TypeAdapter
создается один раз¶
Идея здесь состоит в том, чтобы избегать создания валидаторов и сериализаторов больше, чем необходимо. Каждый раз, когда создается экземпляр TypeAdapter
, он создает новый валидатор и сериализатор. Если вы используете TypeAdapter
в функции, он будет создаваться при каждом вызове функции. Вместо этого создайте его экземпляр один раз и используйте его повторно.
\=== " Плохо"
```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
для определения структуры данных.
??? info «Сравнение производительности» С помощью простого теста TypedDict
примерно в 2,5 раза быстрее, чем вложенные модели:
```py
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)
```
Избегайте валидаторов переноса, если вы действительно заботитесь о производительности.¶
Валидаторы Wrap обычно работают медленнее, чем другие валидаторы. Это связано с тем, что они требуют, чтобы данные материализовались в Python во время проверки. Валидаторы-обертки могут быть невероятно полезны для сложной логики проверки, но если вы ищете максимальную производительность, вам следует их избегать.
Ранний сбой с FailFast
¶
Начиная с версии 2.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
читайте здесь.
本文总阅读量次