メインコンテンツまでスキップ

「Python」タグの記事が12件件あります

Pythonプログラミング言語、フレームワーク、データサイエンス

全てのタグを見る

Python Pydantic

· 約4分
Mikyan
白い柴犬

Pydantic は、Pythonのクラス構文と型ヒント(Type Hint)を活用して、以下のような堅牢なデータ処理を可能にするライブラリです。

  • データの検証(Validation): 型ヒントに基づき、値の型や制約条件を自動チェック

  • 型の自動変換(Coercion): 可能であれば、入力値を期待される型に自動変換

  • データのシリアライズ/デシリアライズ: 辞書やJSONからのモデル生成、辞書やJSON形式での出力

  • バリデーションロジックのカスタマイズ: フィールド単位・モデル単位で検証ロジックを定義可能

1. モデルの定義

基本は BaseModel を継承したクラスを作るだけです。必要に応じて Field() を使うことで、制約(例: 最大文字数、正の数など)や説明文を追加できます。

from typing import Optional
from pydantic import BaseModel, Field, EmailStr

class User(BaseModel):
name: str
age: int
email: str

# Valid data
user = User(name="Alice", age=30, email="[email protected]")
print(user)

# 不正なデータは自動的にエラーになります:

try:
User(name="Bob", age="twenty", email="bob@invalid")
except Exception as e:
print(e)

2. フィールド制約と説明文の付与

class Product(BaseModel):
id: int = Field(..., gt=0, description="Unique product identifier")
name: str = Field(..., min_length=2, max_length=100)
price: float = Field(..., gt=0.0)
description: Optional[str] = None # Optional field
seller_email: EmailStr # Pydantic's built-in email validation

product = Product(id=1, name="Laptop", price=1200.50, seller_email="[email protected]")
print(product)

3. モデルの生成方法

  • 辞書からの検証付きインスタンス生成
  • JSON文字列からの生成

辞書からの作成は、Constructorまたはmodel_validateを使って、モデルを作成できます。

model_validate_json はJsonの文字列でモデルを作成できます。

user_data = {
"name": "Alice",
"age": 30,
"email": "[email protected]"
}
user_model = User(**user_data)

user_model = User.model_validate(user_data)


class Movie(BaseModel):
title: str
year: int
director: str
genres: list[str]

# Your JSON string data
json_string = '''
{
"title": "Inception",
"year": 2010,
"director": "Christopher Nolan",
"genres": ["Sci-Fi", "Action", "Thriller"]
}
'''
movie_model = Movie.model_validate_json(json_string)

4. モデルのシリアライズ: model_validate(), model_validate_json()

  • model_dump: Python辞書へ変換
  • model_dump_json: JSON文字列で出力
from pydantic import BaseModel

class City(BaseModel):
name: str
population: int

tokyo = City(name="Tokyo", population=14000000)
print(tokyo.model_dump())
print(tokyo.model_dump_json(indent=2)) # Pretty print JSON

5. カスタムバリデーション

  • @field_validator 関数を使って、フィールド単位のバリデータ
  • @model_validator 関数を使って、モデル全体の検証
from datetime import date
from pydantic import BaseModel, ValidationError, field_validator, model_validator

class Event(BaseModel):
name: str
start_date: date
end_date: date

@field_validator('name')
@classmethod
def check_name_is_not_empty(cls, v):
if not v.strip():
raise ValueError('Event name cannot be empty')
return v

@model_validator(mode='after') # 'after' means after field validation
def check_dates_order(self):
if self.start_date > self.end_date:
raise ValueError('Start date must be before end date')
return self

try:
event1 = Event(name="Conference", start_date="2025-07-20", end_date="2025-07-22")
print(event1)
except ValidationError as e:
print(e)

try:
Event(name="Bad Event", start_date="2025-07-25", end_date="2025-07-23")
except ValidationError as e:
print(e)

6. 入れ子モデル(ネスト構造)

from pydantic import BaseModel
from typing import List

class Address(BaseModel):
street: str
city: str
zip_code: str

class Customer(BaseModel):
customer_id: int
name: str
shipping_addresses: List[Address]

customer_data = {
"customer_id": 123,
"name": "Jane Doe",
"shipping_addresses": [
{"street": "123 Main St", "city": "Anytown", "zip_code": "12345"},
{"street": "456 Oak Ave", "city": "Otherville", "zip_code": "67890"}
]
}

customer = Customer.model_validate(customer_data)
print(customer)

7. JSON Schemaの自動生成

from pydantic import BaseModel

class Task(BaseModel):
id: int
title: str
completed: bool = False

print(Task.model_json_schema(indent=2))

おわりに

Pydanticは以下のような用途に特に有効です:

  • APIのリクエスト・レスポンスモデル(FastAPIとの統合が特に強力)

  • 設定ファイルや外部入力の安全な読み込み

  • 型安全で自己文書化されたデータモデルの構築

  • 静的型チェックが弱いPythonにおいて、Pydanticは強力な型バリデーションと開発者体験を提供してくれます。

参考リンク

document on how to use it.

Python Type Hint

· 約2分
Mikyan
白い柴犬

Since Python 3.5, Python introduced Type hint. And it become more and more powerful.

With it You can set the type for your variable for readibility.

Type hints are hints, not enforcements. Python still runs the code even if types don't match.

Usage

# Primitives
name: str = "Tom"
age: int = 30
salary: float = 500.5
is_active: bool = True

# Collections
numbers: list = [1,2,3]
scores: tuple = (90, 85, 88)
unique: set = {1, 2, 3}
data: dict = {"key": "value"}


# Specific Collection Types

from typing import List, Dict, Tuple, Set

names: List[str] = ["Alice", "Bob", "Charlie"]
user: Dict[str, str] = {
"name": "John",
"email": "[email protected]"
}
person: Tuple[str, int, bool] = ("Alice", 30, True)
unique_ids: Set[int] = {1, 2, 3, 4, 5}

# after python 3.9 the following are also work
names: list[str] = ["Alice", "Bob", "Charlie"]
user: dict[str, str] = {
"name": "John",
"email": "[email protected]"
}person: tuple[str, int, bool] = ("Alice", 30, True)
unique_ids: set[int] = {1, 2, 3, 4, 5}

# Optional

from typing import Optional

# can be string or None
middle_name: Optional[str] = None

# Union
from typing import Union
number: Union[int, float] = 10
number = 10.5


# Literal for exact values
from typing import Literal
Status = Literal["pending", "approved", "rejected"]

def process_order(status: Status) -> None:
pass

# TypedDict
from typing import TypedDict
# TypedDict for dictionary structures
class UserDict(TypedDict):
name: str
age: int
email: str


# Class
user: User = get_user(123)

# method
def calculate_bmi(weight: float, height: float) -> float:
return weight / (height ** 2)

# Self
from typing import Self

class User:
def copy(self) -> Self: # Returns same class type
return User()