Conseils de performances¶
Dans la plupart des cas, Pydantic ne sera pas votre goulot d'étranglement, suivez-le uniquement si vous êtes sûr que c'est nécessaire.
En général, utilisez model_validate_json()
et non model_validate(json.loads(...))
¶
Sur model_validate(json.loads(...))
, le JSON est analysé en Python, puis converti en dict, puis validé en interne. D'un autre côté, model_validate_json()
effectue déjà la validation en interne.
Il existe quelques cas où model_validate(json.loads(...))
peut être plus rapide. Plus précisément, lors de l'utilisation d'un validateur 'before'
ou 'wrap'
sur un modèle, la validation peut être plus rapide avec la méthode en deux étapes. Vous pouvez en savoir plus sur ces cas particuliers dans cette discussion .
De nombreuses améliorations de performances sont actuellement en cours pour pydantic-core
, comme indiqué ici . Une fois ces modifications fusionnées, nous devrions être au point où model_validate_json()
est toujours plus rapide que model_validate(json.loads(...))
.
TypeAdapter
instancié une fois¶
L’idée ici est d’éviter de construire des validateurs et des sérialiseurs plus que nécessaire. Chaque fois qu'un TypeAdapter
est instancié, il construira un nouveau validateur et sérialiseur. Si vous utilisez un TypeAdapter
dans une fonction, il sera instancié à chaque appel de la fonction. Au lieu de cela, instanciez-le une fois et réutilisez-le.
\=== "Mauvais"
```py
from typing import List
from pydantic import TypeAdapter
def my_func():
adapter = TypeAdapter(List[int])
# do something with adapter
```
\=== "Bien"
```py
from typing import List
from pydantic import TypeAdapter
adapter = TypeAdapter(List[int])
def my_func():
...
# do something with adapter
```
Sequence
vs list
ou tuple
- Mapping
vs dict
¶
Lors de l'utilisation Sequence
, Pydantic appelle isinstance(value, Sequence)
pour vérifier si la valeur est une séquence. De plus, Pydantic tentera de valider différents types de séquences, comme list
et tuple
. Si vous savez que la valeur est une list
ou tuple
, utilisez list
ou tuple
au lieu de Sequence
.
La même chose s'applique à Mapping
et dict
. Si vous savez que la valeur est un dict
, utilisez dict
au lieu de Mapping
.
N'effectuez pas de validation lorsque vous n'êtes pas obligé de le faire - utilisez Any
pour conserver la valeur inchangée¶
Si vous n'avez pas besoin de valider une valeur, utilisez Any
pour conserver la valeur inchangée.
from typing import Any
from pydantic import BaseModel
class Model(BaseModel):
a: Any
model = Model(a=1)
Évitez les informations supplémentaires via des sous-classes de primitives¶
\=== "Ne fais pas ça"
```py
class CompletedStr(str):
def __init__(self, s: str):
self.s = s
self.done = False
```
\=== "Faites ceci"
```py
from pydantic import BaseModel
class CompletedModel(BaseModel):
s: str
done: bool = False
```
Utilisez l'union étiquetée, pas l'union¶
Une union taguée (ou union discriminée) est une union avec un champ qui indique de quel type il s'agit.
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'
)
Voir Syndicats discriminés pour plus de détails.
Utiliser TypedDict
sur des modèles imbriqués¶
Au lieu d'utiliser des modèles imbriqués, utilisez TypedDict
pour définir la structure des données.
??? info "Comparaison des performances" Avec un simple benchmark, TypedDict
est environ 2,5 fois plus rapide que les modèles imbriqués:
```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)
```
Évitez les validateurs wrap si vous vous souciez vraiment des performances¶
Les validateurs Wrap sont généralement plus lents que les autres validateurs. En effet, ils exigent que les données soient matérialisées en Python lors de la validation. Les validateurs Wrap peuvent être incroyablement utiles pour une logique de validation complexe, mais si vous recherchez les meilleures performances, vous devriez les éviter.
Échouer tôt avec FailFast
¶
À partir de la version 2.8+, vous pouvez appliquer l'annotation FailFast
aux types de séquence pour qu'ils échouent plus tôt si un élément de la séquence échoue à la validation. Si vous utilisez cette annotation, vous n'obtiendrez pas d'erreurs de validation pour le reste des éléments de la séquence en cas d'échec, vous échangez donc efficacement la visibilité contre les performances.
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]
"""
En savoir plus sur FailFast
ici.
本文总阅读量次