بررسی کتابخانه محبوب Pydantic
Pydantic: فراتر از اعتبارسنجی
بررسی ۱۰ ویژگی قدرتمند کتابخانهای که مدیریت دادهها در پایتون مدرن را متحول کرده است.
(بر اساس Pydantic نسخه ۲)
در دنیای واقعی، دادهها کثیف و غیرقابل اعتماد هستند. نوشتن دستی کد برای بررسی و تمیز کردن این دادهها کابوس است. Pydantic با استفاده از Type Hintهای استاندارد پایتون، این فرآیند را به یک تجربه لذتبخش، سریع و ایمن تبدیل میکند. در ادامه ۱۰ دلیل اصلی برای استفاده از آن را بررسی میکنیم.
فلسفه اصلی: استفاده از Type Hintها به عنوان Schema
برخلاف بسیاری از کتابخانهها که نیاز به یادگیری زبان تعریف اسکمای جدید دارند، Pydantic از همان سینتکس استاندارد پایتون (که از نسخه 3.6 معرفی شد) استفاده میکند. شما یک کلاس از `BaseModel` ارثبری میکنید و متغیرها را با تایپشان تعریف میکنید. همین!
from pydantic import BaseModel
# تعریف مدل با استفاده از تایپهای استاندارد
class User(BaseModel):
id: int
username: str
is_active: bool = True # مقدار پیشفرض
# استفاده از مدل
user = User(id=123, username="ali_reza")
print(user.username) # دسترسی آسان با داتنوتیشن
تجزیه و تبدیل هوشمند (Smart Parsing & Coercion)
شعار Pydantic این است: "Parse, don't validate". این یعنی به جای اینکه فقط خطا بگیرد، سعی میکند داده ورودی را به تایپ هدف تبدیل کند. اگر رشته "123" را به فیلدی که int است بدهید، Pydantic آن را به عدد تبدیل میکند. این ویژگی در کار با APIها و فرمهای وب که همه چیز رشته است، حیاتی است.
class Product(BaseModel):
price: float
available: bool
# دادههای خام از یک درخواست HTTP (همه رشته هستند)
raw_data = {"price": "99.50", "available": "yes"}
product = Product(**raw_data)
print(product.price) # خروجی: 99.5 (float)
print(product.available) # خروجی: True (bool)
# Pydantic مقادیری مثل "yes", "on", "1" را به True تبدیل میکند.
سرعت بینظیر با هسته Rust (در نسخه 2)
در نسخه دوم، هسته اصلی Pydantic با زبان Rust بازنویسی شد (پروژهای به نام pydantic-core). این تغییر باعث افزایش سرعت چشمگیر (بین 5 تا 50 برابر سریعتر از نسخه 1) شده است. این یعنی پردازش دادهها در مقیاس بالا دیگر گلوگاه برنامه شما نخواهد بود.
مدیریت ساختارهای تو در تو و پیچیده
دادههای واقعی معمولاً ساختار درختی دارند (مثلاً یک فاکتور که شامل لیستی از آیتمهاست). Pydantic به راحتی با استفاده از مدلهای دیگر به عنوان تایپ، این ساختارها را مدیریت، اعتبارسنجی و تبدیل میکند.
from typing import List
class Address(BaseModel):
city: str
class UserWithAddress(BaseModel):
name: str
# استفاده از یک مدل دیگر به عنوان تایپ
address: Address
# لیستی از مدلها
tags: List[str]
data = {
"name": "Sara",
"address": {"city": "Tehran"}, # دیکشنری تو در تو
"tags": ["admin", "123"]
}
user = UserWithAddress(**data)
print(user.address.city) # دسترسی عمیق: Tehran
پشتیبانی عالی از تایپهای استاندارد پایتون
Pydantic به صورت ذاتی از تایپهای پیچیدهتر کتابخانه استاندارد پایتون مانند datetime, date, UUID, Enum و Path پشتیبانی میکند و رشتههای ورودی را به این آبجکتهای قدرتمند تبدیل میکند.
from datetime import datetime
from uuid import UUID
from enum import Enum
class Status(Enum):
ACTIVE = "active"
INACTIVE = "inactive"
class LogEntry(BaseModel):
id: UUID
timestamp: datetime
status: Status
raw_log = {
"id": "123e4567-e89b-12d3-a456-426614174000", # رشته UUID
"timestamp": "2023-10-27T15:30:00Z", # رشته ISO 8601
"status": "active" # رشته Enum
}
log = LogEntry(**raw_log)
# حالا log.timestamp واقعاً یک آبجکت datetime است
print(log.timestamp.year) # 2023
تایپهای تخصصی (ایمیل، URL و ...)
برای اعتبارسنجیهای رایج، نیازی به نوشتن Regex نیست. Pydantic تایپهای آمادهای مثل EmailStr, HttpUrl, IPvAnyAddress و غیره دارد که صحت فرمت داده را تضمین میکنند.
from pydantic import BaseModel, EmailStr, HttpUrl, ValidationError
class Contact(BaseModel):
email: EmailStr
website: HttpUrl
try:
Contact(email="invalid-email", website="htps://typo.com")
except ValidationError as e:
print(e.json())
# خطای دقیق برای هر دو فیلد ایمیل و وبسایت برمیگرداند
اعتبارسنجیهای سفارشی (Custom Validators)
وقتی تایپهای پیشفرض کافی نیستند، میتوانید با استفاده از دکوریتور @field_validator منطق خود را اعمال کنید. این توابع میتوانند داده را تغییر دهند (نرمالسازی) یا خطا برگردانند.
from pydantic import BaseModel, field_validator
class Account(BaseModel):
username: str
# اعمال قانون خاص روی فیلد username
@field_validator('username')
@classmethod
def must_be_alphanumeric(cls, v: str) -> str:
if not v.isalnum():
raise ValueError('نام کاربری فقط باید شامل حروف و اعداد باشد')
return v.lower() # نرمالسازی به حروف کوچک
acc = Account(username="Ali123")
print(acc.username) # ali123
سریالسازی انعطافپذیر (خروجی گرفتن)
تبدیل آبجکتهای پایتون به JSON (مخصوصاً وقتی شامل datetime هستند) همیشه دردسر دارد. Pydantic با متدهای model_dump() (برای دیکشنری) و model_dump_json() (برای رشته JSON) این کار را به سادگی و با قابلیتهای کنترلی بالا (مثل حذف فیلدهای خاص) انجام میدهد.
from datetime import datetime
class Event(BaseModel):
title: str
time: datetime
secret_code: str
event = Event(title="Meeting", time=datetime.now(), secret_code="XYZ")
# خروجی JSON استاندارد، با حذف فیلد secret_code
json_output = event.model_dump_json(exclude={'secret_code'})
print(json_output)
# Output: {"title":"Meeting", "time":"2023-10-27T..."}
مدیریت نامهای مستعار (Aliases) و ادغام سیستمها
در پایتون نام استاندارد snake_case است، اما در جاوااسکریپت (فرانتاند) camelCase رایج است. Pydantic با استفاده از Aliasها پل ارتباطی بین این دو دنیا میشود، بدون اینکه کد پایتون شما زشت شود.
from pydantic import BaseModel, Field
class UserProfile(BaseModel):
# ورودی JSON دارای کلید firstName است، اما در پایتون first_name استفاده میشود
first_name: str = Field(alias="firstName")
last_name: str = Field(alias="lastName")
# داده از فرانتاند
js_data = {"firstName": "Kaveh", "lastName": "Tehrani"}
user = UserProfile(**js_data)
print(user.first_name) # استفاده از نام پایتونی
# هنگام خروجی گرفتن هم میتوان از alias استفاده کرد
print(user.model_dump_json(by_alias=True))
# {"firstName":"Kaveh", "lastName":"Tehrani"}
مدیریت تنظیمات پروژه (Settings Management)
با استفاده از پکیج جانبی pydantic-settings، میتوانید تنظیمات برنامه را مستقیماً از متغیرهای محیطی (Environment Variables) یا فایلهای .env بخوانید، اعتبارسنجی کنید و به تایپهای صحیح (مثل int برای پورت یا bool برای حالت دیباگ) تبدیل کنید. این بهترین روش برای پیادهسازی اپلیکیشنهای ۱۲ فاکتوری است.
from pydantic_settings import BaseSettings
class Settings(BaseSettings):
db_host: str = "localhost" # مقدار پیشفرض
db_port: int # الزامی (حتما باید عدد باشد)
api_key: str
debug: bool = False
model_config = SettingsConfigDict(env_file =
"my_file.env") # خواندن خودکار از فایل .env
# اگر در محیط، DB_PORT="5432" و DEBUG="true" باشد:
settings = Settings()
print(settings.db_port) # 5432 (int)
print(settings.debug) # True (bool)
هنوز هیچ دیدگاهی ثبت نشده است!