mcpField
P
pydantic

pydantic

github.com/pydantic開発1452.2k 回インストール2 週間前に更新
リポジトリ

pydantic について

Pydantic は型ヒントに基づいて実行時の検証とシリアライズを行う Python ライブラリです。外部または信頼できないデータを扱う用途に向け、フィールド制約や型変換、複数の検証方法を備えています。

主な機能

  1. 01型ヒントに基づく検証
  2. 02データのシリアライズ
  3. 03フィールドのメタデータと制約
  4. 04型変換
  5. 05独自および注釈型バリデーター

活用シーン

  1. 01HTTP API が受け取るデータの検証
  2. 02外部データの型付きモデルへの解析

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/pydantic/skills --skill pydantic

インストール後、Claude Code で/pydanticを入力して読み込まれたか確認します。

詳細

Pydantic によるバリデーション

一言でいうと、Pydantic は実行時バリデーション機能を備えたデータクラスです。型ヒントを利用して、バリデーション(およびシリアライズ)の方法を判断します。主に、HTTP API の定義など、信頼できない外部データを扱う場合に役立ちます。

ユーザーコード内でインスタンス化するクラスの定義に Pydantic を使うことは、一般に 推奨されません。そうすると柔軟性が失われます(たとえば、Pydantic がサポートしていない型を使えなくなり、初期化後の変更も難しくなります)。この場合は通常、通常のクラス(または標準ライブラリのデータクラス)を使うほうが適切です。静的型チェッカーが型の不一致を検出してくれるためです。

基本的な使い方

Pydantic モデルを使った簡単な例を次に示します。

from datetime import date

from pydantic import BaseModel, Field


class Person(BaseModel):
    name: str
    age: int = Field(description='The age of the person')
    birthdate: date | None = None


p = Person(name='John', age=20, birthdate='1970-01-01')

Pydantic は互換性のある入力を型強制します。ISO 形式の日付文字列 '1970-01-01' は date に変換されます。

制約とフィールドメタデータ

Field() 関数はメタデータと制約の指定に使います。次の 2 種類のメタデータを区別する必要があります。

  • フィールド固有のメタデータ:deprecated や alias のように、フィールドに付与された場合にのみ意味を持つメタデータです。
  • 型固有のメタデータ:gt や max_length などの制約に加え、JSON Schema に影響するメタデータ(例:description、title)が含まれます。

モデルフィールドは、代入形式の Field() を使って宣言します。

from pydantic import BaseModel, Field


class User(BaseModel):
    first_name: str = Field(alias='name')

または、注釈パターンを使って宣言します。

from typing import Annotated

from pydantic import BaseModel, Field


class Model(BaseModel):
    value: Annotated[int, Field(deprecated=True)] = 1

注釈パターンには、次のような利点があります。

  • f: <type> = Field() の形式(デフォルト値なし)は紛らわしく、実際には必須フィールドであるにもかかわらず、f にデフォルト値があると誤解させる可能性があります。
  • フィールドには、任意の数のメタデータ要素を指定できます。上の例で示したように、Field() 関数がサポートする制約やメタデータは限られているため、場合によっては WithJsonSchema など、別の Pydantic ユーティリティを使う必要があります。

ただし、次の点に注意してください。

  • 静的型チェッカーにとって意味のあるメタデータには、代入形式を使ってください。これには alias、default、default_factory が含まれます。

  • フィールド固有のメタデータは、「最上位」の型にのみ使用できます。よくある落とし穴は、次のように記述することです。

    from typing import Annotated
    
    from pydantic import BaseModel, Field
    
    
    class Model(BaseModel):
        field_bad: Annotated[int, Field(deprecated=True)] | None = None
        field_ok: Annotated[int | None, Field(deprecated=True)] = None
    

    この例では、フィールド固有のメタデータはユニオン全体に適用してください。

制約

可能な限り、カスタムバリデーターを定義するのではなく、「組み込み」のバリデーション制約を使ってください。

from typing import Annotated

from annotated_types import Gt  # annotated_types is an alternative to the `Field()` function.
from pydantic import BaseModel, field_validator


class Model(BaseModel):
    constrained_int_ok: Annotated[int, Gt(1)]  # This is good

    constrained_int_bad: int

    @field_validator('constrained_int_bad')  # This is bad
    @classmethod
    def validate(cls, v: int) -> int:
        if not v > 1:
            raise ValueError('Value is not greater than 1')
        return v

Field() 関数では表現できない制約もあります。たとえば、strip_whitespace、to_upper、to_lower、ascii_only などの文字列制約は、pydantic.StringConstraints を使ってのみ指定できます。

from typing import Annotated

from pydantic import BaseModel, StringConstraints


class Model(BaseModel):
    # Do this instead of a validator calling s.strip():
    a: Annotated[str, StringConstraints(strip_whitespace=True)]

https://pydantic.dev/docs/validation/latest/api/pydantic/standard_library_types/ は、サポートされているすべての標準ライブラリ型とその制約についての公式ドキュメントです。

バリデーター

状況によっては、カスタムバリデーターが必要になることがあります。可能な限り、after バリデーターを使ってください。Pydantic のバリデーション後に実行されるため、値はすでにフィールドの型になっています。before バリデーターを使う場合、入力データは文字どおり何でもあり得るため、誤りが起きやすくなります(特にモデルバリデーターでは、入力は必ずしも辞書とは限らず、任意のオブジェクトの場合もあります)。

可能であれば、バリデーターには注釈パターンを使うことをおすすめします。

from typing import Annotated

from pydantic import AfterValidator, BaseModel, field_validator


def is_even(value: int) -> int:
    if value % 2 == 1:
        raise ValueError(f'{value} is not an even number')
    return value


class Model(BaseModel):
    # Prefer this form: the validator is right next to the field, making it easy to understand
    even: Annotated[int, AfterValidator(is_even)]
    odd: int

    # If you define a validator as decorator, make sure to define it as classmethod.
    @field_validator('odd', mode='after')
    @classmethod
    def is_odd(cls, value: int) -> int:
        if value % 2 == 0:
            raise ValueError(f'{value} is not an odd number')
        return value

デコレーターパターンを使うと、特にバリデーターが実行される順序(とりわけサブクラスの場合)について、動作がわかりにくくなることがあります。

型の強制変換、コレクション、ユニオン

厳密モードを使用していない場合、Pydantic はほとんどのケースで型の強制変換を適用します。たとえば、int 型のフィールドでは、'123' のような文字列も受け入れられます。これはコレクション型にも当てはまり、list[str] ではタプルやセットなども受け入れられます。

そのため、次のことは避けてください。

  • バリデーターを使って str を int に強制変換することが目的であれば、int | str のようなユニオンを使うこと。
  • リストとタプルの両方を受け入れることが目的であれば、collections.abc.Sequence のような抽象コレクションを使うこと。こうした抽象コレクションの使用は非効率です。

一般に、ユニオンは避けるのが最善です。フィールドを使うたびに、何らかの処理を行う前に各型を確認する必要があるためです。

前方参照アノテーション

Python では、文字列を使ってアノテーションを前方参照として記述できます。これにより Pydantic での評価が難しくなる場合があるため、可能であれば避けるのが最善です。

モジュール内で Pydantic モデルを定義する場合、可能であれば from __future__ import annotations(すべてのアノテーションをデフォルトで文字列化します)の使用を避けてください。まだ定義されていないアノテーションにのみ、明示的に引用符を付けてください。例:

from pydantic import BaseModel


class Model(BaseModel):
    self_ref: 'Model'

また、Python >= 3.14 ではアノテーションの評価が遅延されるため、文字列アノテーションは使用しないでください。

再帰的な型エイリアス

次のようなエイリアスを定義したくなるかもしれません。

from typing import TypeAlias

JsonValue: TypeAlias = 'list[JsonValue] | dict[str, JsonValue] | str | bool | int | float | None'

再帰的なため、このエイリアスは引用符で囲む必要があります。Pydantic は通常、引用符で囲まれた TypeAlias を評価できません。代わりに、Pydantic が解決できる明示的な型エイリアス(Python 3.12 以降では type、または TypeAliasType)を使用してください。

type JsonValue = list[JsonValue] | dict[str, JsonValue] | str | bool | int | float | None
# Or, if not on Python >= 3.12:
from typing_extensions import TypeAliasType

JsonValue = TypeAliasType('JsonValue', 'list[JsonValue] | dict[str, JsonValue] | str | bool | int | float | None')
モデルのサブクラス、判別可能ユニオン

サブクラス化は Python で非常によく使われるパターンですが、Pydantic では問題の原因になることがあります。次のようにしたくなるかもしれません。

from pydantic import BaseModel


class Base(BaseModel):
    base_field: int

    def common_method(self) -> None: ...


class Sub1(Base):
    sub1_field: str


class Sub2(Base):
    sub2_field: bool


class Main(BaseModel):
    model: Base


m: Main = Main(model=Sub1(base_field=1, sub1_field='test'))

この例は動作しますが、m のシリアル化時に期待どおりには動作しません。

m.model_dump()
#> {'model': {'base_field': 1}} -> sub1_field missing

これは、Pydantic が実行時のサブクラスではなく、宣言された型(Base)に従ってシリアル化するためです。検証も同じ規則に従います。Main(model={'base_field': 1, 'sub1_field': 'test'}) は Base に対して検証されるため、Sub1 インスタンスが生成されるのではなく、sub1_field は無視されます。

代わりに、判別可能ユニオン(モデルを区別するために type フィールドを設定できる場合)を使ってみてください。

from typing import Annotated, Literal, TypeAlias

from pydantic import BaseModel, Field


class Sub1(Base):
    type: Literal['sub1']
    sub1_field: str


class Sub2(Base):
    type: Literal['sub2']
    sub2_field: bool


Subs: TypeAlias = Annotated[Sub1 | Sub2, Field(discriminator='type')]


class Main(BaseModel):
    model: Subs

または、ジェネリクスを使ってください。

from pydantic import BaseModel


class Main[BaseT: Base](BaseModel):
    model: BaseT


m: Main[Sub1] = Main[Sub1](model={'base_field': 1, 'sub1_field': 'test'})  # Will work

判別可能ユニオンもジェネリクスも適さない場合は、最後の手段として、Pydantic >=2.13 ではポリモーフィックシリアル化、Pydantic <2.13 では任意の型としてシリアル化を使用できます。

コメント