Day 5 · ~13m

Required Fields vs Defaults

Understanding required fields, default values, and default_factory for mutable defaults.

🧑‍💻

How does Pydantic decide if a field is required or optional? Is it just whether I give it a default value?

👩‍🏫

Exactly. If a field has no default value, it's required — you must provide it when creating the model. If it has a default, it's optional:

from pydantic import BaseModel

class Config(BaseModel):
    host: str              # required — no default
    port: int = 8080       # optional — defaults to 8080
    debug: bool = False    # optional — defaults to False

# Only host is required
c = Config(host="localhost")
print(c.port)   # 8080
print(c.debug)  # False

Required fields must come before optional fields in the class definition. Python won't let you put a field without a default after one that has one — same rule as function arguments.

🧑‍💻

What if my default is a mutable thing like a list? I've heard that's dangerous in regular Python.

👩‍🏫

Great instinct. In regular Python, a mutable default is shared across all instances — a classic trap. Pydantic handles simple cases like list[str] = [] safely by copying the default. But for anything more complex, use default_factory:

from pydantic import BaseModel, Field

class Pipeline(BaseModel):
    name: str
    steps: list[str] = Field(default_factory=list)
    config: dict[str, str] = Field(default_factory=dict)

default_factory=list means "call list() to create a fresh empty list for each new instance." No shared state, no surprises.

🧑‍💻

When would I use default_factory instead of just = []?

👩‍🏫

For simple defaults like = [] or = {}, Pydantic copies them safely, so either approach works. Use default_factory when your default is expensive to compute or when you want to be explicit about creating a new object each time:

from datetime import datetime

class Event(BaseModel):
    title: str
    created_at: datetime = Field(default_factory=datetime.now)
    attendees: list[str] = Field(default_factory=list)

e1 = Event(title="Launch")
print(e1.created_at)  # current timestamp
print(e1.attendees)   # []

The default_factory=datetime.now is evaluated at creation time — each Event gets its own timestamp.

🧑‍💻

Can I mix required and optional fields freely as long as required ones come first?

👩‍🏫

Yes. The pattern is: all required fields first, then all fields with defaults. Pydantic follows Python's rules here:

class Order(BaseModel):
    # Required
    customer_id: str
    items: list[str]
    # Optional (with defaults)
    status: str = "pending"
    notes: str = ""
    priority: int = 0

order = Order(customer_id="C123", items=["widget", "gadget"])
print(order.status)    # "pending"
print(order.priority)  # 0

This is a pattern you'll use in every model — required fields define what must be provided, defaults define sensible fallbacks.

Practice your skills

Sign up to write and run code in this lesson.

Already have an account? Sign in