導入:問いは「どれが最強か」ではなく「何が要るか」
「Python のデータモデルは結局どれを使えばいい? Pydantic? dataclass?」——これはよく聞かれる質問ですが、問い自体が少しずれています。5 つの選択肢は、解いている問題が違うからです。dataclasses と Pydantic を「どちらが優れているか」で比べるのは、ドライバーとインパクトレンチを比べるようなものです。
本記事は、dataclasses・TypedDict・attrs・msgspec・Pydantic の 5 つを、公式の一次情報に基づいて公平に比較します。Pydantic を売り込むための記事ではありません——むしろ「ここでは Pydantic は要らない」という判断こそ、エンジニアの信頼性を示します。marshmallow との比較は marshmallow vs Pydantic で扱っているので、本記事は Python 標準ライブラリ+速度特化ライブラリとの比較に絞ります。
最初に、選定を貫くたった一つの問いを提示します——「そのデータは、信頼できない外部から来るか?」。来るなら、実行時に値を検証するライブラリが要ります。来ない(コード内部で生成・管理される信頼できるデータ)なら、検証は不要なコストです。この軸を念頭に読み進めてください。
1. 全体像:5 つを一枚の表で
まず結論の俯瞰図です。各セルは各プロジェクトの公式情報に基づきます。
| 軸 | Pydantic v2 | dataclasses | TypedDict | attrs | msgspec |
|---|---|---|---|---|---|
| 実行時検証 | ✅ 中核機能 | ❌ なし | ❌ なし(静的のみ) | △ オプトイン | ✅ デコード時 |
| 直列化(JSON等) | ✅ 内蔵 | △ asdictのみ(JSON非対応) | ❌(実体は dict) | ❌(別途 cattrs) | ✅ JSON/msgpack/YAML/TOML |
| JSON Schema 生成 | ✅ | ❌ | ❌ | ❌ | ✅ |
| 性能の位置づけ | 「最速級」/Rust製コア | 検証なし=オーバーヘッドなし | 同上 | 公称なし | 速度特化(公称最速級) |
| 設定管理 | ✅ pydantic-settings | ❌ | ❌ | ❌ | ❌ |
| 標準 / サードパーティ | サードパーティ | 標準 | 標準 | サードパーティ | サードパーティ |
| 主な用途 | API・境界・設定・LLM出力 | 内部の構造体 | 辞書の静的型付け | 柔軟なクラス生成 | 高スループット直列化 |
ポイントは最初の 2 行です。「実行時検証」と「直列化」の有無が、選定をほぼ決めます。以下、1 つずつ公式情報で確認します。
2. dataclasses:標準ライブラリの構造体(検証はしない)
Python 標準の @dataclass は、__init__ / __repr__ / __eq__ などを自動生成します。依存ゼロで構造化レコードが書けるのが最大の利点です。
from dataclasses import dataclass
@dataclass
class InventoryItem:
name: str
unit_price: float
quantity_on_hand: int = 0
ただし、型アノテーションは実行時に検証されません。公式ドキュメントは明言しています——「(2 つの例外を除き)@dataclass はアノテーションで指定された型を一切調べない」。
# 型は str/float/int だが、検証されないので "壊れた" インスタンスが普通に作れる
item = InventoryItem(name=123, unit_price="無料", quantity_on_hand=None) # エラーにならない
asdict / astuple で dict・tuple へ変換できますが、JSON 直列化や JSON Schema 生成はありません。
💡
dataclassesを選ぶとき:データの出どころが信頼できる内部(自分のコードが生成する設定オブジェクト、計算の中間結果、DTO など)で、検証も直列化も要らない場合。標準ライブラリだけで完結し、依存を増やさない——KISS と YAGNI に最も忠実な選択です。
3. TypedDict:辞書に「静的な型」を付ける
TypedDict は、辞書のキーと値に型ヒントを与えます。しかし実行時には何も起きません。公式の言葉どおり——「実行時には TypedDict のインスタンスは単なる dict」であり、「この期待は実行時には検査されず、型チェッカーによってのみ強制される」。
from typing import TypedDict
class Point2D(TypedDict):
x: int
y: int
label: str
p: Point2D = {"x": 1, "y": 2, "label": "A"} # 実体はただの dict。mypy だけが型を見る
💡
TypedDictを選ぶとき:JSON 由来の辞書ペイロードに静的型チェック(mypy/Pyright)だけを効かせたい場合。クラスを増やさず、実行時の振る舞いも変えずに、エディタ補完と静的検査を得られます。なお Pydantic はTypedDictを検証対象にできるので、「静的にはTypedDict、境界では Pydantic で検証」という併用も可能です(パフォーマンス最適化ガイド で触れたとおり、Pydantic 公式も「TypedDictはネストモデルより約 2.5 倍速い」と認めています)。
4. pydantic.dataclasses:dataclass 構文に検証を足す
「dataclass の書き心地のまま、検証だけ欲しい」——その中間解が pydantic.dataclasses です。標準の @dataclass を Pydantic 版に差し替えると、検証と型強制が効くようになります。
from datetime import datetime
from typing import Optional
from pydantic.dataclasses import dataclass
@dataclass
class User:
id: int
name: str = "John Doe"
signup_ts: Optional[datetime] = None
user = User(id="42", signup_ts="2032-06-21T12:00")
# id="42" → 42 に型強制、signup_ts は datetime にパースされる
公式は*「BaseModel を使いたくない場合、標準の dataclass で同じデータ検証が得られる」と説明しつつ、「Pydantic dataclass は Pydantic モデルの置き換えではない」*とも明記しています。model_config の一部や BaseModel のメソッド群(model_dump 等)はそのままは使えません。
💡
pydantic.dataclassesを選ぶとき:既存の dataclass ベースのコードに、最小の変更で検証を導入したい場合。あるいは「dataclass の構文が好きだが、外部入力の検証は欲しい」場合。新規で API 境界をがっつり作るなら、次章以降のBaseModelのほうが機能が揃っています。
5. attrs:成熟したクラスビルダー(検証はオプトイン、直列化は別)
attrs は dataclass の源流ともいえる成熟ライブラリで、クラス生成を強力かつ柔軟に制御できます(slots・コンバータ・バリデータなど)。検証はオプトイン——バリデータを明示的に付けたフィールドだけが検証されます。
from attrs import define, field
import attrs
@define
class Color:
value = field(validator=attrs.validators.instance_of(int))
@value.validator
def _fits_byte(self, attribute, value):
if not 0 <= value < 256:
raise ValueError("0〜255 の範囲で指定してください")
重要なのは、attrs は直列化ライブラリではないこと。公式が*「attrs は本格的な直列化ライブラリではない…姉妹プロジェクトの cattrs を見てほしい」*と述べているとおり、JSON との相互変換は cattrs が担います(関心の分離)。JSON Schema 生成も標準ではありません。
💡
attrsを選ぶとき:クラス生成を細かく制御したい(スロット最適化、コンバータ、複雑な__init__ロジック)、かつ直列化を意図的に分離したい(cattrs と組む)場合。Pydantic より「クラスの作り方」そのものへの自由度が高い一方、検証・スキーマ・エコシステムは自分で組み立てる必要があります。
6. msgspec:速度に全振りした検証+直列化
msgspec は、直列化と検証の速度に特化したライブラリです。msgspec.Struct を定義すると、JSON・MessagePack・YAML・TOML の相互変換と、デコード時の型検証が極めて高速に行われます。
import msgspec
class User(msgspec.Struct):
name: str
groups: set[str] = set()
email: str | None = None
msgspec.json.encode(User("alice", groups={"admin"}))
# b'{"name":"alice","groups":["admin"],"email":null}'
msgspec.json.decode(b'{"name":"bob","groups":[123]}', type=User)
# msgspec.ValidationError: Expected `str`, got `int` - at `$.groups[0]`
msgspec は JSON Schema 生成(msgspec.json.schema)も備えます。性能について、公式は強気な数値を自社ベンチマークとして掲げています——「JSON/MessagePack 実装は Python で最速級」「msgspec は orjson が単にデコードするより速くデコード&検証する」「構造体は一般的な操作で 5〜60 倍速い」。
⚠️ 数値はベンダー公称である:これらの倍率は msgspec 自身のベンチマークによる主張であり、第三者が中立に測ったものではありません。自分のワークロードで測ってから採用してください。一方 Pydantic 公式は性能ページの冒頭で*「多くの場合 Pydantic はボトルネックにならない」*と述べています。「速さが本当に律速になっているか」を計測で確かめるのが先決です(Pydantic パフォーマンス最適化ガイド 参照)。
💡
msgspecを選ぶとき:高スループットな API・メッセージキュー・巨大 JSON の取り込みで、直列化+検証が実測のボトルネックになっている場合。速度特化のサードパーティを導入できるなら強力です。
7. Pydantic:検証・スキーマ・エコシステムのバランス
Pydantic は、実行時検証・JSON Schema 生成・pydantic-settings・広大なエコシステムを一つにまとめた、いわば「境界の既定値」です。中核の pydantic-core は Rust 製で、公式は*「Python で最速級のデータ検証ライブラリの一つ」*と位置づけます。
from pydantic import BaseModel, Field
class User(BaseModel):
id: int
name: str = Field(min_length=1)
email: str
User.model_validate({"id": "42", "name": "alice", "email": "a@example.com"})
# 検証+型強制+(必要なら)JSON Schema 生成+直列化がワンストップ
Pydantic の真の強みは、機能単体よりもエコシステムにあります。FastAPI・SQLModel・Django Ninja・LangChain・PydanticAI が Pydantic を土台にしており、PyPI 上で約 8,000 のパッケージが Pydantic に依存しています。「検証したモデルが、そのまま API スキーマになり、設定になり、LLM の構造化出力になる」——この一気通貫が、他にはない価値です。
💡 Pydantic を選ぶとき:外部入力を扱う境界(HTTP API、外部 API レスポンス、設定、LLM 出力)。検証・直列化・スキーマ・設定を一貫した型で扱いたい場合。FastAPI を使うなら事実上の標準です。「迷ったら Pydantic」が成立するのは、この守備範囲の広さゆえです。
8. 意思決定フローチャート
最後に、選定を 1 枚のフローに落とします。
- 実行時に値を検証する必要があるか?(データは信頼できない外部から来るか)
- いいえ → 2 へ。
- はい → 3 へ。
- (検証不要)辞書のまま静的型付けしたい?
- はい →
TypedDict - いいえ(クラスが欲しい)→
dataclasses(細粒度の制御が要るならattrs)
- はい →
- (検証必要)直列化の速度が実測のボトルネックか?
- はい →
msgspec - いいえ → 4 へ。
- はい →
- JSON Schema・設定管理・FastAPI 等のエコシステムが欲しい?
- はい → Pydantic(新規 API 境界の既定)
- dataclass 構文を保ちたいだけ →
pydantic.dataclasses - クラス生成の自由度+直列化分離が欲しい →
attrs+ cattrs
| 状況 | 推奨 |
|---|---|
| FastAPI で API を作る | Pydantic(事実上の標準) |
| 設定・シークレット管理 | pydantic-settings(ガイド) |
| LLM の構造化出力 | Pydantic / PydanticAI(ガイド) |
| 検証不要な内部 DTO | dataclasses |
| 辞書の静的型付け | TypedDict |
| 直列化が律速の高スループット | msgspec |
| クラス生成の細かな制御 | attrs + cattrs |
結論:道具は問題に合わせて選ぶ
5 つの選択肢は競合ではなく、解く問題のレイヤーが違う道具です。本記事の要点を再掲します。
- 選定の第一問は**「外部入力を検証するか」。
dataclassesとTypedDictは検証しない**(公式明記)。 dataclassesは標準の構造体、TypedDictは辞書の静的型付け——いずれも信頼できる内部データ向け。attrsは検証オプトイン+直列化分離(cattrs)、msgspecは速度特化(ただし倍率はベンダー公称)。- Pydantic は検証・スキーマ・設定・エコシステムのバランスで、境界の既定値。
- 迷ったらフローチャート——検証の要否 → 速度の律速 → エコシステムの必要性、の順で絞る。
「最強のライブラリ」を探すより、目の前のデータがどこから来て、何を保証したいのかを見極めることが、保守性とコスト効率の高い設計につながります。それが分かっていれば、Pydantic を選ぶときも、あえて標準の dataclass で済ませるときも、自信を持って説明できます。
各プロジェクトの一次情報:
技術選定・データモデリング設計のご相談
筆者は、経済産業大臣賞を受賞した B2B SaaS をはじめ、複数の本番システムで「どこに何の道具を使うか」の技術選定を主導してきました。データモデリングの選定は、性能・保守性・チームの学習コスト・エコシステムを天秤にかける意思決定です。要件に対して過不足のないライブラリ選定と、それに基づく型安全なアーキテクチャ設計を、生成 AI を活用して高速かつ高品質に支援します。Python バックエンドの技術選定について、お気軽にご相談ください。