Performance tips¶
ほとんどの場合、Pydanticはボトルネックにはならないので、それが必要だと確信している場合にのみ利用してください。
In general, use model_validate_json()
not model_validate(json.loads(...))
¶
model_validate(json.loads(...))
では、JSONがPythonで解析され、dictに変換されてから内部で検証されます。
一方、model_validate_json()
はすでに内部で検証を実行しています。
model_validate(json.loads(...))
の方が高速な場合がいくつかあります。具体的には、モデルに対して'before'
または'wrap'
バリデータを使用する場合、2ステップ法の方が検証が高速になる可能性があります。
これらの特殊なケースの詳細については、this discussionを参照してください。
現在、pydantic-core
では、[here]で議論されているように、多くのパフォーマンス改善が行われている(https://github.com/pydantic/pydantic/discussion/6388#discussioncomment-8194048)。
これらの変更がマージされれば、model_validate_json()
が常にmodel_validate(json.loads(...))
よりも高速になるはずです。
TypeAdapter
instantiated once¶
ここでの考え方は、必要以上にバリデーターとシリアライザーを構築しないようにすることです。
TypeAdapter
がインスタンス化されるたびに、新しいバリデータとシリアライザが構築されます。
関数でTypeAdapter
を使用している場合、関数が呼び出されるたびにインスタンス化されます。代わりに、一度インスタンス化してから再利用してください。
from typing import List
from pydantic import TypeAdapter
def my_func():
adapter = TypeAdapter(List[int])
# do something with adapter
from typing import List
from pydantic import TypeAdapter
adapter = TypeAdapter(List[int])
def my_func():
...
# do something with adapter
Sequence
vs list
or tuple
- Mapping
vs dict
¶
Sequence
を使用する場合、Pydanticはisinstance(value, Sequence)
を呼び出して、値がシーケンスかどうかをチェックします。
また、Pydanticはlist
やtuple
のような異なるタイプのシーケンスに対して検証を試みます。
値がlist
またはtuple
であることがわかっている場合は、Sequence
の代わりにlist
またはtuple
を使用してください。
同じことがMapping
とdict
にも当てはまります。
値がdict
であることがわかっている場合は、Mapping
の代わりにdict
を使用してください。
Don't do validation when you don't have to - use Any
to keep the value unchanged¶
値を検証する必要がない場合は、Any
を使用して値を変更しないようにします。
from typing import Any
from pydantic import BaseModel
class Model(BaseModel):
a: Any
model = Model(a=1)
Avoid extra information via subclasses of primitives¶
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
Use tagged union, not union¶
タグ付きユニオン(または識別されたユニオン)は、それがどのタイプであるかを示すフィールドを持つユニオンです。
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'
)
詳細については、Discriminated Unionsを参照してください。
Use TypedDict
over nested models¶
ネストされたモデルを使用する代わりに、TypedDict
を使用してデータの構造を定義してください。
Performance comparison
単純なベンチマークでは、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)
Avoid wrap validators if you really care about performance¶
一般に、ラップ・バリデーターは他のバリデーターよりも低速です。 これは、検証時にPythonでデータを実体化する必要があるためです。ラップ・バリデーターは複雑な検証ロジックには非常に便利ですが、最高のパフォーマンスを求めるのであれば、ラップ・バリデーターは避けるべきです。
Failing early with 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
についての詳細はこちらhereをご覧ください。