Pydantic(パイダンティック)は、Pythonの型ヒントを書くだけで、入力データの検証と型変換をまとめて行うライブラリです。BaseModel を継承したクラスに id: int と書けば、文字列の "42" は整数の42に変換され、"abc" はどのフィールドがなぜ不正かを示す ValidationError になります。本記事のコードは Pydantic 2.14.2(2026年10月29日公開、2026年11月4日時点の最新安定版)と Python 3.13 で実行して確認し、出力は必要な部分を抜粋して載せています。
まとめ:Pydanticの要点
- 型ヒントを検証ルールとして使うデータ検証ライブラリ。読み方は「パイダンティック」が一般的
- 最新安定版は2.14.2で、2.14からPython 3.10以上が必要。V2から検証の中核(pydantic-core)がRust製になった
- 基本は
BaseModelを継承し、制約はField()、独自ルールはfield_validatorとmodel_validatorで書く - 必須かどうかは既定値の有無で決まる。
int | Noneと書いても既定値が無ければ必須で、V1とはここが違う - JSONの読み込みは
model_validate_json()、書き出しはmodel_dump_json()。V1のparse_obj()・.dict()・.json()は非推奨 - 既定では “42”→42 のような変換をするが、V2は 4.9→4 の切り捨てや 123→”123″ の変換をしない
Pydanticの位置付けと読み方
名前の由来と読み方
公式ドキュメントは、Pydanticという名前を「Py」と「pedantic」のかばん語(2語を縮めてつないだ語)だと説明しています。pedanticは「細かいことにうるさい、杓子定規な」という意味の英単語で、データの型に細かく口を出すライブラリの性格をそのまま表しています。
日本語では「パイダンティック」と読み、表記するのが一般的です。公式が日本語の読みを定めているわけではありませんが、pedanticの英語の発音(ペダンティック)に、Pythonの「パイ」を重ねた読み方です。
最新版2.14とRust製のpydantic-core
PyPIのpydanticのページで配布されている最新の安定版は2.14.2(2026年10月29日)で、動作要件は Python 3.10 以上です。2026年10月8日の2.14.0で Python 3.9 のサポートが打ち切られ、Python 3.15 への対応が加わりました。3.9の環境で pip install pydantic を実行すると、エラーにはならず 3.9 に対応する最後の系列である2.13系が入ります。
2023年のV2で内部が作り直され、検証処理の中核は別パッケージの pydantic-core(Rust製)に移りました。公式のV2発表記事に載った開発時のベンチマークでは、一般的なフィールドを組み合わせたモデルの検証で、V1比約17倍という結果が示されています。Web上の古い解説に「Cythonで高速化」とあるのはV1時代の話です。2.14.2には pydantic-core 2.50.2 が組み合わされます。
利用規模について、公式のGet Startedページは「月5億5,000万回以上ダウンロードされている」と書いています。FastAPI、Hugging Face、SQLModel、LangChain のような有名ライブラリも、内部でPydanticを使っています。
型ヒント・dataclassとの違い
Pythonの型ヒントそのものは実行時に何も検査しません。標準ライブラリの dataclass も同じで、次のように int と宣言したフィールドに文字列を渡しても、そのまま通ります。
from dataclasses import dataclass
@dataclass
class DP:
x: int
y: str
print(DP(x="abc", y=1))
# DP(x='abc', y=1)
mypyやPylanceのような静的型チェッカーは、コード中で型に合わない値を渡す誤りを実行前に見つけます。ただし、APIのリクエストやCSV、環境変数のように実行時に外から入ってくる値は対象外です。Pydanticはこの実行時の境界を受け持ちます。型ヒントは同じ書き方のまま、外部入力を検証・変換するのがPydanticの役割です。
| 手段 | 検査の時点 | 型変換 | JSON入出力 |
|---|---|---|---|
| 型ヒント+mypy | 実行前 | しない | なし |
| dataclass | 検査しない | しない | なし(自作) |
| TypedDict | 実行前のみ | しない | なし |
| Pydantic BaseModel | 実行時 | する(laxモード) | 組み込み |
dataclassやTypedDictのままPydanticの検証だけ使うこともできます。pydantic.dataclasses.dataclass や TypeAdapter がその入口です。名前付きタプルやdataclassの選び分けはnamedtupleとdataclass・dictの違いで扱っています。
インストールとBaseModelの基本形
本記事と同じ版を使う場合は python -m pip install "pydantic==2.14.2" でインストールします。メールアドレス型(EmailStr)を使うときは python -m pip install "pydantic[email]==2.14.2" で email-validator を追加します。次が最小の例です。
from datetime import date
from pydantic import BaseModel, Field, ValidationError
class User(BaseModel):
id: int
name: str = Field(min_length=1, max_length=20)
email: str
signup: date
tags: list[str] = []
u = User(id="42", name="Sato", email="[email protected]", signup="2026-11-04")
print(repr(u))
# User(id=42, name='Sato', email='[email protected]', signup=datetime.date(2026, 11, 4), tags=[])
print(u.model_dump())
# {'id': 42, 'name': 'Sato', 'email': '[email protected]', 'signup': datetime.date(2026, 11, 4), 'tags': []}
文字列で渡した id は int に、signup は datetime.date に変換されています。Field() は既定値と制約をまとめて書く関数で、min_length・max_length(文字列やリストの長さ)、gt・ge・lt・le(数値の範囲)、pattern(正規表現)、alias(外部データ側のキー名)がよく使う引数です。
tags: list[str] = [] のように可変の既定値を直接書けるのも、dataclassとの違いです。Pydanticはリストなどハッシュ化できない既定値をインスタンスごとにディープコピーするため、この例ではリストが共有されません。
決まった値しか取らない項目は Literal、メールアドレスは EmailStr で宣言します。EmailStr は形式を検査するだけでなく、ドメイン部を小文字にそろえます。
from typing import Literal
from pydantic import BaseModel, EmailStr
class Account(BaseModel):
role: Literal["admin", "user"]
mail: EmailStr
print(Account(role="user", mail="[email protected]"))
# role='user' mail='[email protected]'
Account(role="guest", mail="x")
# role: Input should be 'admin' or 'user' [type=literal_error]
# mail: value is not a valid email address: An email address must have an @-sign. [type=value_error]
必須と任意の決まり方:既定値とOptional
V2では、フィールドが必須かどうかは既定値を書いたかどうかだけで決まります。型に None を含めても、それは「Noneを値として受け付ける」という意味で、省略してよいという意味にはなりません。4通りの書き方で試した結果です。
from typing import Optional
from pydantic import BaseModel, ValidationError
class P(BaseModel):
a: int | None # 必須。Noneは渡せる
b: int | None = None # 任意。省略するとNone
c: int = None # 任意だが、既定値のNoneは検証されない
d: Optional[int] = None # bと同じ(古い書き方)
try:
P()
except ValidationError as e:
print(e.errors()[0]["loc"], e.errors()[0]["type"])
# ('a',) missing
print(P(a=None)) # a=None b=None c=None d=None
P(a=None, c=None) # loc=('c',) type=int_type
| 書き方 | 省略 | Noneを渡す |
|---|---|---|
| a: int | None | missing(必須) | 通る |
| b: int | None = None | None | 通る |
| c: int = None | None(検証されない) | int_type |
V1から移ってきた人が詰まるのが a の行です。同梱の pydantic.v1 で a: Optional[int] だけを書いたモデルを作ると、V1は省略を許して a=None にします。V2では同じ定義が必須になり、省略すると missing エラーです。V1時代のコードで「Optionalなのに必須と言われる」ときは、= None を書き足します。
c: int = None は避けてください。Pydanticは既定では既定値を検証しないため、省略時は型と食い違う None が黙って入ります。一方で明示的に None を渡すと int_type エラーになり、同じNoneでも結果が分かれます。Noneを許すなら int | None = None と型に書きます。
更新APIのように「項目が送られてこなかった」と「Noneで消したい」を区別したい場合は、2.14のリリース記事で安定版になった MISSING を既定値にします。省略された項目は出力からも消えます。
from pydantic import BaseModel, MISSING
class Patch(BaseModel):
name: str | None | MISSING = MISSING
print(Patch().model_dump()) # {}
print(Patch(name=None).model_dump()) # {'name': None}
既定値を default_factory で作る書き方や、Field() に渡せる引数の用途別一覧は「Pydantic Fieldの使い方:default_factory・alias・制約の引数一覧とOpenAPIへの反映」で扱っています。
ValidationErrorの読み方
不正な値を渡すと、Pydanticは最初のエラーで止まらず、すべてのフィールドを検査してからまとめて報告します。
try:
User(id="abc", name="", email="x", signup="2026-13-01")
except ValidationError as e:
print(e.error_count()) # 3
print(e)
3 validation errors for User
id
Input should be a valid integer, unable to parse string as an integer [type=int_parsing, input_value='abc', input_type=str]
For further information visit https://errors.pydantic.dev/2.14/v/int_parsing
name
String should have at least 1 character [type=string_too_short, input_value='', input_type=str]
For further information visit https://errors.pydantic.dev/2.14/v/string_too_short
signup
Input should be a valid date or datetime, month value is outside expected range of 1-12 [type=date_from_datetime_parsing, input_value='2026-13-01', input_type=str]
For further information visit https://errors.pydantic.dev/2.14/v/date_from_datetime_parsing
各エラーは「フィールド名」「英語のメッセージ」「type= のエラー種別」の3点で読みます。email="x" はエラーになっていません。str と宣言しただけでは形式を検査しないためで、メール形式を強制するには前の章の EmailStr を使います。末尾のURLは版ごとに分かれた公式のエラー種別の解説へつながっています。
プログラムで分岐するときは、メッセージ文ではなく e.errors() が返す辞書の type と loc を見ます。メッセージは版で変わることがありますが、int_parsing のような種別名は変わりにくいためです。
try:
User(id="abc", name="", email="x", signup="2026-13-01")
except ValidationError as e:
print(e.errors()[0])
# {'type': 'int_parsing', 'loc': ('id',), 'msg': 'Input should be a valid integer, unable to parse string as an integer', 'input': 'abc', 'url': 'https://errors.pydantic.dev/2.14/v/int_parsing'}
自動変換の範囲:laxモードの実測とV1との差
Pydanticは既定で「laxモード」で動き、型ごとの変換規則に従って入力を宣言した型に変換します。ただし、変換で情報が失われる場合もあります。どこまでが変換され、どこからがエラーかは、ドキュメントを読むより表で見たほうが早いので、TypeAdapter(型).validate_python(値) で1件ずつ確かめました。右列は strict=True を付けた場合です。
| 宣言した型 | 入力 | laxモード(既定) | strictモード |
|---|---|---|---|
| int | “42” | 42 | int_type |
| int | ” 42 “ | 42 | int_type |
| int | “1_000” | 1000 | int_type |
| int | 4.0 | 4 | int_type |
| int | 4.5 | int_from_float | int_type |
| int | True | 1 | int_type |
| float | 3 | 3.0 | 3.0 |
| bool | “yes” / “on” / “1” | True | bool_type |
| bool | 2 | bool_parsing | bool_type |
| str | 123 | string_type | string_type |
| str | b”abc” | “abc” | string_type |
| date | “2026-09-28” | date(2026, 9, 28) | date_type |
| date | “2026/09/28” | date_from_datetime_parsing | date_type |
| date | 0 | date(1970, 1, 1) | date_type |
| list[int] | (“1”, “2”) | [1, 2] | list_type |
表で英字だけのセルは、エラーになったときの種別名です。見落としやすいのは次の3点です。int 型に True を渡すと1として通ります。日付はスラッシュ区切りの「2026/09/28」がエラーになる一方、数値の0はUnix時刻とみなされて1970年1月1日になります。そして str 型に数値の123を渡すとエラーです。
最後の点はV1からの変更です。同じ環境に同梱されている pydantic.v1 で試すと、V1は123を "123" に変換し、int 型に4.9を渡すと4に切り捨てていました。V2はどちらもエラーにします。V1時代の記事のコードがV2で動かない場合、この変換規則の差が原因の一つになります。
なお、2026年10月29日に出た1.10.27について、GitHubのリリースノートは「最後のV1リリースで、V2に同梱の pydantic.v1 名前空間も含めてV1のサポートはこれで完全に終わる」と書いています。2.14.2の pydantic.v1 は1.10.27相当で、今は読み込めますが、比較や段階移行の足場として使うものと考えてください。
from pydantic.v1 import BaseModel as V1Model
class V1(V1Model):
s: str
i: int
print(V1(s=123, i=4.9))
# s='123' i=4 ← V2では s も i も検証エラー
変換を止めたいフィールドには Field(strict=True) を付けます。JSONを model_validate_json() で読む場合は、strictモードでも "2026-09-28" のような文字列から date への変換は許されます(JSONに日付型が無いため)。モデル全体へのstrictの掛け方とV1からの設定の移し替えは、Pydantic v2のConfigDict設定一覧にまとめています。
field_validatorとmodel_validatorによる独自ルール
型と Field() の制約で書けないルールはバリデーターで足します。1つのフィールドだけを見るなら @field_validator、複数のフィールドを突き合わせるなら @model_validator です。
from datetime import date
from pydantic import BaseModel, field_validator, model_validator
class Reservation(BaseModel):
guest: str
check_in: date
check_out: date
@field_validator("guest")
@classmethod
def strip_guest(cls, v: str) -> str:
v = v.strip()
if not v:
raise ValueError("氏名が空です")
return v
@model_validator(mode="after")
def check_dates(self):
if self.check_out <= self.check_in:
raise ValueError("check_out は check_in より後の日付にしてください")
return self
print(Reservation(guest=" 田中 ", check_in="2026-10-01", check_out="2026-10-03"))
# guest='田中' check_in=datetime.date(2026, 10, 1) check_out=datetime.date(2026, 10, 3)
バリデーターは ValueError を送出すれば検証エラーとして扱われます。上のfield_validatorは加工後のフィールド値を返し、mode=”after”のmodel_validatorは検証後のモデル自身を返します。上の例では前後の空白を落とした「田中」が保存されています。日付を逆にすると、Value error, check_out は check_in より後の日付にしてください [type=value_error, ...] という1件のエラーになります。
mode="after" は型変換が済んだ後に動くという意味で、self.check_in はすでに date 型です。変換前の生の値に手を入れたいときは mode="before" を使います。V1の @validator と @root_validator はV2では非推奨で、置き換え先が上の2つです。@root_validator は既定のまま(pre=False)書くとクラス定義の時点で PydanticUserError になり、skip_on_failure=True を付けないと動きません。
同じルールを複数のモデルで使うなら、デコレーターより Annotated で型に付けるほうが再利用しやすくなります。
from typing import Annotated
from pydantic import AfterValidator, TypeAdapter
def even(v: int) -> int:
if v % 2:
raise ValueError("偶数ではありません")
return v
EvenInt = Annotated[int, AfterValidator(even)]
print(TypeAdapter(list[EvenInt]).validate_json("[2, 4, 6]")) # [2, 4, 6]
# "[2, 3]" を渡すと loc=(1,) の位置で「偶数ではありません」
JSON・辞書との相互変換とJSON Schema
V2で名前が変わったメソッドの対応です。表に示したV1形式のメソッドは2.14でも利用できますが、PydanticDeprecatedSince20 の非推奨警告の対象です。警告の表示頻度はPythonの警告フィルター設定によります。
| 処理 | V2(現行) | V1(非推奨) |
|---|---|---|
| 辞書から生成 | Model.model_validate(d) | Model.parse_obj(d) |
| JSON文字列から生成 | Model.model_validate_json(s) | Model.parse_raw(s) |
| 辞書へ出力 | m.model_dump() | m.dict() |
| JSON文字列へ出力 | m.model_dump_json() | m.json() |
| JSON Schema出力 | Model.model_json_schema() | Model.schema() |
| 検証なしで生成 | Model.model_construct() | Model.construct() |
JSONを読むときは json.loads() で辞書にしてから model_validate() に渡すより、文字列のまま model_validate_json() に渡します。公式のパフォーマンスガイドも通常はこの書き方を勧めており、JSONの解析とモデルの検証がpydantic-coreの中で済みます。ただし、mode="before" や mode="wrap" のバリデーターを使う場合は、先に json.loads() で辞書にするほうが速くなることがあると同じガイドに書かれています。ネストしたモデルの場合、エラーの loc は深さまで示します。
from pydantic import BaseModel, Field
class Item(BaseModel):
sku: str
qty: int = Field(gt=0)
class Order(BaseModel):
order_id: int
items: list[Item]
Order.model_validate_json('{"order_id": 1, "items": [{"sku": "A-1", "qty": 2}, {"sku": "B-2", "qty": 0}]}')
# ValidationError: loc=('items', 1, 'qty') type=greater_than
2件目の明細の qty が0だと、('items', 1, 'qty') という経路で場所が返ります。APIのエラーレスポンスにそのまま載せられる形です。
model_dump_json() は date を "2026-11-04" のような文字列に変換して出力します。一方、model_dump() は date オブジェクトのまま返すので、辞書を json.dumps() に渡すと TypeError: Object of type date is not JSON serializable で失敗します。JSONにしたいなら最初から model_dump_json() か model_dump(mode="json") を使います。
モデル定義からJSON Schemaも出せます。Item.model_json_schema() の結果は次のとおりで、Field(gt=0) が exclusiveMinimum に反映されています。
{"properties": {"sku": {"title": "Sku", "type": "string"}, "qty": {"exclusiveMinimum": 0, "title": "Qty", "type": "integer"}}, "required": ["sku", "qty"], "title": "Item", "type": "object"}
Pydanticが使われる場面
FastAPIのリクエストとレスポンス
FastAPIはエンドポイントの引数に BaseModel を書くと、リクエストボディの検証、422エラーの応答、OpenAPIドキュメントの生成をすべてPydanticのモデルから行います。FastAPIのリリースノートによると、0.126.0(2025年12月20日)で単体のPydantic V1のサポートを、0.128.0(2025年12月27日)で pydantic.v1 名前空間のサポートも終了しました。現行のFastAPIでは、V2の書き方で定義したモデルしか使えません。FastAPIの仕組みと始め方はFastAPIの概要記事で解説しています。データベースのテーブル定義とPydanticモデルを1つのクラスで兼ねたい場合は、SQLModelが選択肢になります。
BaseModelをPOSTのリクエストボディとして受ける実装と、検証に失敗したときの422のdetailの読み方は、FastAPIでPOSTを受け取る方法にコード付きでまとめています。
外部システムとつなぐAPIでは、受け取るJSONの形をPydanticのモデルとして先に決めておくと、仕様書(OpenAPI)と実装の食い違いが起きにくくなります。社内システムや取引先とのデータ連携をAPIで組む場合の設計から実装までは、API開発・システム連携の支援でもご相談いただけます。
環境変数と設定ファイル(pydantic-settings)
V1では本体に入っていた BaseSettings は、V2から別パッケージの pydantic-settings(pip install pydantic-settings)に移りました。環境変数を型付きで読み込み、"true" をboolに、"9000" をintに変換します。
from pydantic_settings import BaseSettings, SettingsConfigDict
class Settings(BaseSettings):
model_config = SettingsConfigDict(env_prefix="APP_")
debug: bool = False
port: int = 8000
# 環境変数 APP_DEBUG=true APP_PORT=9000 のとき
print(Settings())
# debug=True port=9000
.envとの読み込みの優先順位、他のツールと共用する.envでの扱い、SecretStrによるシークレットの保持、FastAPIへの注入とテストでの差し替えは、pydantic-settingsの使い方:BaseSettingsで.env・環境変数・シークレットを型安全に読むで詳しく解説しています。
LLM構造化出力でのJSON Schema生成と検証
生成AIのAPIでJSONを返させる用途でもPydanticは定番です。モデルクラスからJSON Schemaを作ってAPIに渡し、返ってきたJSONを同じクラスで検証します。OpenAIのPython SDK、LangChain、Pydantic AIがこの形を採っています。仕組みと実装はLLM構造化出力の解説を、Pydantic社のエージェント用フレームワークはPydantic AIの解説を参照してください。
Pydanticを使わないほうがよい場面
Pydanticは外部から来るデータの入口で使う道具で、アプリケーション内部の値の受け渡しにまで使う必要はありません。検証にはコストがかかります。この記事の実行環境(Intel Core i9-9880H・Python 3.13.14・Pydantic 2.14.2)で、x: int と y: str を持つモデルに同じ辞書をキーワード引数として渡し、timeit.repeat で20万回の生成を5回測定して最小値を採ったところ、1件あたり dataclass は約0.8マイクロ秒、BaseModel は約2.2マイクロ秒でした。
今回のモデルと入力データでは、1件ずつモデルに通すより、リスト全体を1回で渡すほうが速くなりました。同じモデルに対し、入力は [{"x": i, "y": str(i)} for i in range(100000)] とそのJSON文字列を使いました。TypeAdapterの構築時間は含めず、各処理を timeit.repeat で1回ずつ7回測った最小値です。
| 10万件の検証方法 | 所要時間 |
|---|---|
| json.loads()のあと1件ずつmodel_validate() | 387.5 ms |
| 辞書のリストを1件ずつmodel_validate() | 362.9 ms |
| TypeAdapter(list[M]).validate_json() | 227.5 ms |
| TypeAdapter(list[M]).validate_python() | 197.5 ms |
リスト型の TypeAdapter にまとめて渡すと、Pythonのループを回さずにpydantic-coreの中で処理が済むため、1件ずつより約1.8倍速くなりました。JSON文字列から読むなら json.loads() を挟まず validate_json() に渡すのが近道です。それでも、すでに検証済みのデータを内部で何度も詰め替える処理や、数百万行を1行ずつモデル化するバッチでは差が積み上がります。次の場合はdataclassやTypedDictで十分です。
- 自分のコードの中だけで作って使う値で、外部入力を含まない
- 入口ですでにPydanticで検証したデータを、内部の関数間で受け渡すだけ
- 大量の表形式データを扱い、列単位の検証(pandasやPolarsの型指定)で足りる
逆に、APIのリクエスト、設定値、外部サービスからのレスポンス、LLMの出力のように「中身を信用できないデータ」が入ってくる境界では、Pydanticで一度検証してから内部に渡す設計が崩れにくくなります。
よくある質問
Pydanticの読み方は?
日本語では「パイダンティック」と読むのが一般的です。公式ドキュメントは名前の由来を、Python の「Py」と、英語で「細かいことにうるさい」を意味する「pedantic」を組み合わせた語だと説明しています。
PydanticのBaseModelとは?
データの形を定義するための基底クラスです。継承したクラスに型ヒント付きでフィールドを書くと、インスタンス生成時に検証と型変換が行われ、model_dump() や model_dump_json() で辞書やJSONに書き出せるようになります。
PydanticはRustで書かれていますか?
V2以降、検証処理の中核である pydantic-core がRustで書かれています。利用者が書くのは普通のPythonのクラスで、Rustの知識は要りません。pip が実行環境に合ったビルド済みのパッケージを入れるため、通常はRustのコンパイラも不要です。
Pydanticとtypingの違いは?
typing は型を「書く」ための標準モジュールで、実行時には何も検査しません。Pydanticはその型ヒントを読み取り、実行時に値を検証・変換します。typingで書いた型を、Pydanticが実行時のルールとして使う関係です。
V1のコードはV2で動きますか?
.dict() や @validator などの多くは非推奨の警告付きで動きます。V1そのもののサポートは2026年10月29日の1.10.27で終了しました。ただし、Optional[int] だけを書いたフィールドが必須になる点や、str 型への数値の変換、int 型への小数の切り捨てのような型変換の規則はV2で変わっており、V1で通っていた入力がエラーになることがあります。