0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Pydantic 忘備録

Last updated at Posted at 2025-01-01

Pydanticの基本

以下をnotebookのcellに入れて実験しましょう。

基本編

準備

  • BaseModel:
    • Pydantic の核心となるクラス
    • これを継承してモデルクラスを作ることで、各種バリデーション機能が利用できるようになります
  • ValidationError:
    • Pydantic が検証時に投げる例外クラス
    • 不正なデータを与えるとこのエラーが発生します
  • User:
    • BaseModel を継承したクラス
    • アノテーション (id: int, name: str など) によって、各フィールドの型を指定します。
    • 必須フィールド(... などを指定していない場合)はアノテーションだけで十分指定可能です。
cell1
from pydantic import BaseModel, ValidationError

class User(BaseModel):
    """
    ユーザー情報を保持するシンプルな Pydantic モデルクラス
    """
    id: int
    name: str
    age: int

正常系のテスト

以下を実行すると

cell2
# 1) 正しいデータを与えてインスタンス化する例
valid_data = {
	"id": 1,
	"name": "Alice",
	"age": 30,
}
try:
	user = User(**valid_data)  # 通常のコンストラクタ
	print("[OK] 正常にインスタンス化:", user)
except ValidationError as e:
	print("[ERROR] ValidationError:", e)
[OK] 正常にインスタンス化: id=1 name='Alice' age=30

異常系のテスト

以下を実行すると

cell2
# 2) 不正なデータ(型不一致や欠落)を与えてインスタンス化する例
invalid_data = {
	"id": "文字列になっている",		# 本来 int が必要
	# "name": "Bob",			# name を敢えて入れない例なども可
	"age": 25,
}
try:
	user = User(**invalid_data)
	print("[OK] 正常にインスタンス化:", user)
except ValidationError as e:
	print("[ERROR] ValidationError:", e)
[ERROR] ValidationError: 2 validation errors for User
id
  Input should be a valid integer, unable to parse string as an integer [type=int_parsing, input_value='文字列になっている', input_type=str]
    For further information visit https://errors.pydantic.dev/2.10/v/int_parsing
name
  Field required [type=missing, input_value={'id': '文字列になっている', 'age': 25}, input_type=dict]
    For further information visit https://errors.pydantic.dev/2.10/v/missing

model_constructメソッドによるバリデーション無しモデル生成

準備

from pydantic import BaseModel, ValidationError, Field

class User(BaseModel):
    """
    Pydantic の基本クラス BaseModel を継承して、
    ユーザー情報を表すためのモデルクラスを定義します。
    """
    
    id: int = Field(..., description="ユーザーID(必須)")
    name: str = Field(..., description="ユーザー名(必須)")
    age: int = Field(0, description="ユーザーの年齢(デフォルトは 0)")

    class Config:
        """
        Config(model_config)クラスでは、
        extra フィールドの扱い方や型チェックの動作などを設定できます。
        """
        extra = "ignore"  # ここでは未知のフィールドが来ても無視します。

通常のインスタンス生成

通常のコンストラクタ(User(**values))によるモデルインスタンス生成を示す関数

  • Pydantic が自動で型・必須フィールドのチェックを行うので安全
  • 不正なデータがあれば ValidationError を投げる
valid_data = {
	"id": 1,
	"name": "Alice",
	"age": 25,
	"extra_field": "このフィールドは ignore 設定により無視される",
}

try:
	# バリデーションを通してインスタンス化
	user = User(**valid_data)
	print(f"[OK] 正常にインスタンス化: {user}")
except ValidationError as e:
	# 型が合わない、必須フィールドがないなど不備があればこのエラーが発生
	print("[ERROR] ValidationError:", e)
[OK] 正常にインスタンス化: id=1 name='Alice' age=25

不正なデータを使ったインスタンス生成

無効なデータ(型が合わない、必須フィールドがないなど)を渡した場合の例。ValidationError が発生する様子を確認します。

invalid_data = {
	"id": "文字列は本来 int ではない",  # 型が合わない
	"name": "Bob",
	# "age" フィールドが欠落している(任意だが、型違いでテストを兼ねている)
}

try:
	user = User(**invalid_data)
	print(f"[OK] 正常にインスタンス化されてしまいました: {user}")
except ValidationError as e:
	# ここでは必ず ValidationError が起こるはず
	print("[ERROR] ValidationError:", e)
[ERROR] ValidationError: 2 validation errors for User
id
  Input should be a valid integer, unable to parse string as an integer [type=int_parsing, input_value='文字列は本来 int ではない', input_type=str]
    For further information visit https://errors.pydantic.dev/2.10/v/int_parsing
age
  Field required [type=missing, input_value={'id': '文字列は本...ない', 'name': 'Bob'}, input_type=dict]
    For further information visit https://errors.pydantic.dev/2.10/v/missing

バリデーション無しでインスタンス生成

Pydantic の model_construct メソッドを利用して、バリデーションを行わずにモデルを生成する方法を示す関数。

  • このメソッドは「信頼できるデータ」あるいは「すでにバリデーション済みのデータ」を再度 Pydantic のモデルにしたい場合など、性能を重視する用途で用いられます
  • バリデーションを一切行わないため、誤ったデータが混在していてもエラーは出ません
trusted_data = {
	"id": 999,        # ここでは int 型で正しい値を入れる
	"name": "Charlie", 
	"age": "30",      # 文字列だが、バリデーションがないためこのまま通ってしまう
	"extra_field": "このフィールドも ignore 設定により無視される",
}

# ↓ バリデーションなしで直接モデルを生成
user = User.model_construct(**trusted_data)
print("[INFO] バリデーションなしで生成した User: ", user)
print("[INFO] 内部の __dict__ を確認:", user.__dict__)  # age が str のまま入っている!
[INFO] バリデーションなしで生成した User:  id=999 name='Charlie' age='30'
[INFO] 内部の __dict__ を確認: {'id': 999, 'name': 'Charlie', 'age': '30'}

ConfigDictを使った例

Pydantic v2 から登場した ConfigDict は、Pydantic v1 系で用いられていた class Config: とほぼ同等の機能を、辞書形式で指定できるようにしたものです。これを model_config というクラス属性に割り当てると、Pydantic モデルの挙動を細かく設定できます。

準備

  • from pydantic import BaseModel, ConfigDict をインポート
  • BaseModel: Pydantic のモデルを作成するときに継承するクラス
  • ConfigDict: モデルの設定(コンフィグ)を辞書形式でまとめるためのヘルパーモデルクラス内で model_config というクラス変数を定義し、ConfigDict を代入
from pydantic import BaseModel, ConfigDict

class User(BaseModel):
    # ここで設定を辞書的にまとめる
    model_config = ConfigDict(
        extra='ignore',           	# 未定義フィールドを無視する
        validate_assignment=True,  	# 既存インスタンスに新たな値を設定した際もバリデーションする
        frozen=True,             	# True にすると、インスタンスのフィールドを変更不可にできる
        title='User Model',       	# スキーマなどで表示されるタイトル
    )

    id: int
    name: str
    age: int = 0

test1: 通常どおりインスタンス化

user_data = {'id': 123, 'name': 'Alice', 'age': 25, 'extra_field': '無視される'}
user = User(**user_data)
print(user)
id=123 name='Alice' age=25

test2: validate_assignment=Trueによる代入時のエラーチェック

validate_assignment=True により、代入時にもチェックが行われることを確認する

user.age = '30'  # int 型以外を入れようとすると ValidationError が起きる
---------------------------------------------------------------------------
ValidationError                           Traceback (most recent call last)
Cell In[25], line 2
      1 # validate_assignment=True により、代入時にもチェックが行われる
----> 2 user.age = '30'  # int 型以外を入れようとすると ValidationError が起きる

File ~/himawari/latest/venv/lib/python3.10/site-packages/pydantic/main.py:911, in BaseModel.__setattr__(self, name, value)
    908             self.__pydantic_private__[name] = value
    909     return
--> 911 self._check_frozen(name, value)
    913 attr = getattr(self.__class__, name, None)
    914 # NOTE: We currently special case properties and `cached_property`, but we might need
    915 # to generalize this to all data/non-data descriptors at some point. For non-data descriptors
    916 # (such as `cached_property`), it isn't obvious though. `cached_property` caches the value
    917 # to the instance's `__dict__`, but other non-data descriptors might do things differently.

File ~/himawari/latest/venv/lib/python3.10/site-packages/pydantic/main.py:986, in BaseModel._check_frozen(self, name, value)
    980     return
    981 error: pydantic_core.InitErrorDetails = {
    982     'type': typ,
    983     'loc': (name,),
    984     'input': value,
    985 }
--> 986 raise pydantic_core.ValidationError.from_exception_data(self.__class__.__name__, [error])

ValidationError: 1 validation error for User
age
  Instance is frozen [type=frozen_instance, input_value='30', input_type=str]
    For further information visit https://errors.pydantic.dev/2.10/v/frozen_instance
0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?