Как определить опциональные поля и значения по умолчанию в Pydantic-модели?
Поля с дефолтом задаются через = значение или Field(default=...), для изменяемых типов — Field(default_factory=list). Optional[T] = None делает поле необязательным и допускающим None; Optional[T] без дефолта — обязательное поле, принимающее None.
Опциональные поля и значения по умолчанию в Pydantic
Pydantic предоставляет несколько способов объявить поле необязательным или задать ему значение по умолчанию. Важно понимать разницу между «поле может быть None» и «поле может отсутствовать в данных».
Значение по умолчанию — простой литерал
from pydantic import BaseModel
class User(BaseModel):
name: str
role: str = "viewer" # по умолчанию 'viewer'
is_active: bool = True # по умолчанию True
score: int = 0
u = User(name="Ivan")
print(u.role) # 'viewer'
print(u.is_active) # True
Optional[T] — поле может быть None
Optional[str] — это синоним str | None. Поле принимает строку или None, но по умолчанию всё равно обязательно, если не задать = None.
from pydantic import BaseModel
from typing import Optional
class Profile(BaseModel):
username: str
bio: Optional[str] = None # необязательное, по умолчанию None
avatar_url: str | None = None # то же самое, современный синтаксис
p = Profile(username="alice")
print(p.bio) # None
print(p.avatar_url) # None
p2 = Profile(username="bob", bio="Python developer")
print(p2.bio) # 'Python developer'
Field() — расширенная настройка
Функция Field() из pydantic позволяет задать дефолт вместе с дополнительными параметрами: описанием, ограничениями, примером.
from pydantic import BaseModel, Field
from typing import Optional
class Product(BaseModel):
name: str = Field(..., min_length=1, max_length=100) # обязательное
price: float = Field(gt=0, description="Price in USD")
discount: Optional[float] = Field(None, ge=0, le=1, description="0.0–1.0")
tags: list[str] = Field(default_factory=list) # изменяемый дефолт
p = Product(name="Widget", price=9.99)
print(p.discount) # None
print(p.tags) # []
default_factory — для изменяемых значений по умолчанию
Никогда не используйте изменяемые объекты (список, словарь) как дефолт напрямую — каждый экземпляр должен получить свою копию. Для этого есть default_factory:
from pydantic import BaseModel, Field
from datetime import datetime
class Event(BaseModel):
name: str
tags: list[str] = Field(default_factory=list)
metadata: dict[str, str] = Field(default_factory=dict)
created_at: datetime = Field(default_factory=datetime.utcnow)
e1 = Event(name="Deploy")
e2 = Event(name="Rollback")
e1.tags.append("prod")
print(e2.tags) # [] — отдельный список, не тот же объект
Поля без значений по умолчанию — обязательные
Поле без дефолта и без Optional обязательно. Попытка создать модель без него вызовет ValidationError:
from pydantic import BaseModel, ValidationError
class Order(BaseModel):
id: int # обязательное
comment: str = "" # необязательное
try:
Order() # нет id
except ValidationError as e:
print(e)
# 1 validation error for Order
# id
# Field required
Разница Optional и Required с None
from pydantic import BaseModel
from typing import Optional
class A(BaseModel):
x: Optional[str] # обязательное, но принимает None — НУЖНО передать явно
y: Optional[str] = None # необязательное, дефолт None
# A() — ValidationError: x is required
# A(x=None) — OK, x=None, y=None
# A(x="hi") — OK, x='hi', y=None
Подводные камни
- Optional без = None не делает поле необязательным —
Optional[str]без дефолта всё равно требует передачи значения (пусть иNone). Это частая ошибка. - Изменяемый дефолт без default_factory —
tags: list = []вызовет ошибку Pydantic v2; используйтеField(default_factory=list). - None vs отсутствие ключа — Pydantic не различает «ключ есть, значение None» и «ключа нет» по умолчанию. Если нужно это различие, используйте
model_config = ConfigDict(populate_by_name=True)и анализируйтеmodel_fields_set. - model_fields_set — атрибут экземпляра, содержащий только те поля, которые были явно переданы при создании. Полезно для PATCH-запросов (частичное обновление).
- Pydantic v1 vs v2 — в v1
Optional[str]автоматически добавлял= None; в v2 это поведение убрали. Миграция с v1 на v2 часто ломает модели именно на этом. - Ellipsis (...) как маркер обязательного поля —
Field(...)явно помечает поле как required; это синтаксис для документирования, не добавляющий дефолт. - Производительность default_factory — фабрика вызывается при каждом создании экземпляра; если фабрика дорогая (например, сетевой запрос), это повлияет на производительность.
Common mistakes
- Описывать optional fields defaults только как термин и не показывать механизм на минимальном примере.
- Игнорировать ошибки, пустые данные, конкурентный доступ или границы транзакции.
- Не связывать поведение с официальным контрактом Pydantic и реальной эксплуатацией.
What the interviewer is testing
- Объясняет optional fields defaults через последовательность действий, а не через набор ключевых слов.
- Приводит короткий кодовый пример или production-сценарий с ожидаемым поведением.
- Называет хотя бы один риск: производительность, безопасность, транзакции, память или сопровождение.