はじめに
この記事では、PythonのデータバリデーションライブラリであるPydanticを使って、簡単にかつ強力にデータのバリデーションを行う方法を解説します。
今回はGoogle Colab上でハンズオン形式で進めていきますので、ブラウザさえあれば実行環境を整えるのも簡単です。
1. Pydanticとは?
- データのバリデーションや型の宣言を簡単に行えるPythonライブラリ
- Pythonの型ヒント(type hints)を活用して、データ構造の定義と検証を同時に実現
- FastAPIなどの人気フレームワークでも広く採用されており、API開発・プロジェクト構成などで非常に便利
Pydanticを使うと、辞書やJSONで受け取ったデータが正しい形式になっているかどうかをPythonicに検証できるようになります。データの整合性を保つために煩雑なチェックを書かなくてよくなるので、とてもおすすめです。
2. Google Colabでの開発準備
まずはGoogle Colabにアクセスし、新しいノートブックを作成します。無料のGoogleアカウントがあれば簡単に利用できます。
2-1. ライブラリのインストール
Google Colabのセルで次のようにして、Pydanticをインストールしましょう。Colab環境は再起動するとインストール済みのライブラリが消えてしまうため、ノートブックを開くたびに必要に応じてインストールします。
!pip install pydantic
3. はじめてのPydanticモデル作成
Pydanticの基本はBaseModelクラスを継承してモデルクラスを作ることです。以下の例では、ユーザープロファイルを表すモデルを定義します。
from pydantic import BaseModel
class User(BaseModel):
name: str
age: int
email: str
- BaseModelを継承したクラスに型ヒント付きで属性を定義する
- インスタンス化するときに、型や必須項目のチェックが自動で行われる
3-1. 簡単な動作確認
実際にインスタンスを作成して動作を確認してみましょう。
# 正しいデータの場合
valid_data = {
"name": "Alice",
"age": 25,
"email": "alice@example.com"
}
user = User(**valid_data)
print(user)
-
User(**valid_data)
で辞書を展開して引数を渡します。 -
print(user)
すると、name='Alice' age=25 email='alice@example.com'
のようにバリデーションを通ったデータが表示されます。
# 間違ったデータの場合
invalid_data = {
"name": "Bob",
"age": "not a number",
"email": "bob@example.com"
}
user = User(**invalid_data)
-
age
が文字列になっているためバリデーションエラー (ValidationError
) が発生します。 - エラーにはどのフィールドがどのように不正なのかが表示されるので、デバッグにも便利です。
4. バリデーションの仕組みと例外処理
Pydanticはインスタンス生成時にバリデーションを行い、不正なデータがあれば例外を投げます。エラーハンドリングしたい場合は、try-except
で囲んで処理しましょう。
from pydantic import ValidationError
try:
user = User(**invalid_data)
except ValidationError as e:
print("Validation Error:")
print(e)
- ValidationErrorオブジェクトには、エラー詳細が格納されている
- どのフィールドでバリデーションに失敗したかがわかりやすい
5. 追加バリデーション – カスタムバリデータ
Pydanticには、カスタムバリデータを定義するための便利な仕組みが用意されています。
たとえば、@validator
デコレータを使って、email
フィールドのドメインを限定するといったことが可能です。
from pydantic import BaseModel, validator
class User(BaseModel):
name: str
age: int
email: str
@validator('email')
def email_must_be_certain_domain(cls, v):
if not v.endswith("@example.com"):
raise ValueError("Email must be on the 'example.com' domain.")
return v
-
@validator('email')
によって、email
フィールドに対するバリデーションを定義 - 条件を満たさない場合はValueErrorを発生させる
試しに次のコードを実行すると、正しいアドレスと誤ったアドレスで結果が変わることを確認できます。
try:
user_ok = User(name="Carol", age=30, email="carol@example.com")
print("バリデーション成功:", user_ok)
user_ng = User(name="Dave", age=40, email="dave@gmail.com")
except ValidationError as e:
print("バリデーション失敗:", e)
6. モデル同士の入れ子(ネスト)を試してみる
複雑なデータ構造を扱いたい場合は、モデルを入れ子にすることができます。
たとえば、Address
モデルとUser
モデルを別々に定義して、User
モデルの中にAddress
モデルを持たせるイメージです。
from pydantic import BaseModel
from typing import Optional
class Address(BaseModel):
city: str
zip_code: str
class UserWithAddress(BaseModel):
name: str
age: int
address: Optional[Address] = None
-
address
フィールドにはAddress
を格納 -
Optional[Address]
にすることで、住所情報が無い場合(None
)でもOKに
data_with_address = {
"name": "Emily",
"age": 28,
"address": {
"city": "Tokyo",
"zip_code": "100-0001"
}
}
user_with_address = UserWithAddress(**data_with_address)
print(user_with_address)
7. まとめ
- Pydanticを使うと、型ヒントを活用して簡潔にデータバリデーションが可能
- ValidationErrorでエラー内容が明確になるため、デバッグやエラー処理もしやすい
- Google Colabならすぐに環境を整えてハンズオンができる
- さらに複雑なモデルやカスタムバリデーションを組み合わせることで、堅牢なデータ構造を実現できる
今回はPydanticの超入門編として、基本的な使い方を紹介しました。次回以降では、ユニオン型を使った柔軟なバリデーションや、設定クラスを使った細かい挙動制御、FastAPIなどでの活用方法などを取り上げていきたいと思います。
参考リンク
動作確認用ノートブック
{
"cells": [
{
"cell_type": "markdown",
"id": "41150395",
"metadata": {},
"source": [
"# Pydantic 動作確認ノートブック\n",
"\n",
"このノートブックでは、Pydanticの基本的な機能を動作確認するためのコードを実行します。"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "34c16a2f",
"metadata": {},
"outputs": [],
"source": [
"!pip install pydantic"
]
},
{
"cell_type": "markdown",
"id": "8de996d2",
"metadata": {},
"source": [
"## Pydanticモデルの作成\n",
"\n",
"まず、基本的なPydanticモデルを作成して動作を確認します。"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "cab5fadb",
"metadata": {},
"outputs": [],
"source": [
"\n",
"from pydantic import BaseModel\n",
"\n",
"class User(BaseModel):\n",
" name: str\n",
" age: int\n",
" email: str\n",
"\n",
"# 正しいデータ\n",
"valid_data = {\n",
" \"name\": \"Alice\",\n",
" \"age\": 25,\n",
" \"email\": \"alice@example.com\"\n",
"}\n",
"\n",
"user = User(**valid_data)\n",
"print(user)\n",
" "
]
},
{
"cell_type": "markdown",
"id": "c56b40bc",
"metadata": {},
"source": [
"## バリデーションエラーの確認\n",
"\n",
"不正なデータを与えるとバリデーションエラーが発生します。"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "80789ccf",
"metadata": {},
"outputs": [],
"source": [
"\n",
"from pydantic import ValidationError\n",
"\n",
"# 間違ったデータ\n",
"invalid_data = {\n",
" \"name\": \"Bob\",\n",
" \"age\": \"not a number\",\n",
" \"email\": \"bob@example.com\"\n",
"}\n",
"\n",
"try:\n",
" user = User(**invalid_data)\n",
"except ValidationError as e:\n",
" print(\"Validation Error:\")\n",
" print(e)\n",
" "
]
},
{
"cell_type": "markdown",
"id": "13daf9b9",
"metadata": {},
"source": [
"## カスタムバリデータの例\n",
"\n",
"次に、`@validator`デコレータを使ったカスタムバリデータの例を試してみます。"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "dde47d60",
"metadata": {},
"outputs": [],
"source": [
"\n",
"from pydantic import validator\n",
"\n",
"class UserWithCustomValidation(BaseModel):\n",
" name: str\n",
" age: int\n",
" email: str\n",
"\n",
" @validator('email')\n",
" def validate_email(cls, value):\n",
" if not value.endswith(\"@example.com\"):\n",
" raise ValueError(\"Email must be on the 'example.com' domain.\")\n",
" return value\n",
"\n",
"# 正しいデータ\n",
"user_valid = UserWithCustomValidation(name=\"Alice\", age=25, email=\"alice@example.com\")\n",
"print(user_valid)\n",
"\n",
"# 間違ったデータ\n",
"try:\n",
" user_invalid = UserWithCustomValidation(name=\"Bob\", age=30, email=\"bob@gmail.com\")\n",
"except ValidationError as e:\n",
" print(\"Validation Error:\")\n",
" print(e)\n",
" "
]
},
{
"cell_type": "markdown",
"id": "885c372e",
"metadata": {},
"source": [
"## 入れ子モデルの例\n",
"\n",
"Pydanticでは入れ子モデルも簡単に扱えます。"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "35275b4b",
"metadata": {},
"outputs": [],
"source": [
"\n",
"from typing import Optional\n",
"\n",
"class Address(BaseModel):\n",
" city: str\n",
" zip_code: str\n",
"\n",
"class UserWithAddress(BaseModel):\n",
" name: str\n",
" age: int\n",
" address: Optional[Address] = None\n",
"\n",
"data_with_address = {\n",
" \"name\": \"Emily\",\n",
" \"age\": 28,\n",
" \"address\": {\n",
" \"city\": \"Tokyo\",\n",
" \"zip_code\": \"100-0001\"\n",
" }\n",
"}\n",
"\n",
"user_with_address = UserWithAddress(**data_with_address)\n",
"print(user_with_address)\n",
" "
]
}
],
"metadata": {},
"nbformat": 4,
"nbformat_minor": 5
}