Default Values and Type Hints in Python Dataclasses

I’ve spent countless hours writing boilerplate for data-holding classes. I remember the days of manually writing __init__, __repr__, and __eq__ methods for every single entity in a project.

It was tedious and often led to small, annoying bugs that were hard to track down during late-night deployments.

Then came Python 3.7 and the introduction of dataclasses, which completely changed how I structure my data models.

In this tutorial, I’ll show you how to use type hints and default values in Python dataclasses to write cleaner, more professional code.

Understand Type Hints in Dataclasses

In a standard Python class, type hints are optional and often ignored by the runtime.

However, in a dataclass, type hints are mandatory because the @dataclass decorator uses them to identify which fields to include.

If you define a variable without a type hint, the dataclass decorator will simply skip it, treating it as a regular class variable.

Let’s look at a practical example involving a real estate listing in the United States.

from dataclasses import dataclass

@dataclass
class PropertyListing:
    address: str
    city: str
    state: str
    zip_code: int
    price: float
    is_active: bool

# Creating an instance
home = PropertyListing(
    address="1600 Pennsylvania Avenue NW",
    city="Washington",
    state="DC",
    zip_code=20500,
    price=0.0,
    is_active=True
)

print(home)

I executed the code above and added the screenshot below.

Default Values and Python Type Hints in Dataclasses

In this snippet, each field has a specific type hint, such as str or int.

This makes the code self-documenting and allows IDEs to provide better autocompletion for your American business applications.

How to Assign Simple Default Values

One of the best features of dataclasses is the ability to assign default values directly to fields.

This is incredibly useful when you have fields that usually stay the same, like a country code for a US-based shipping app.

When you provide a default value, that field becomes optional when you initialize the class.

from dataclasses import dataclass

@dataclass
class USShippingLabel:
    recipient_name: str
    street_address: str
    city: str
    state: str
    weight_oz: float
    carrier: str = "USPS"  # Default value
    country: str = "USA"   # Default value

# Usage without specifying carrier or country
label = USShippingLabel(
    recipient_name="John Doe",
    street_address="742 Evergreen Terrace",
    city="Springfield",
    state="IL",
    weight_oz=12.5
)

print(label)

I executed the code above and added the screenshot below.

Default Values and Type Hints in Python Dataclasses

In my experience, placing defaults at the end is crucial because Python requires non-default arguments to come before default ones.

If you try to put a field without a default after one with a default, Python will throw a TypeError.

Use field() for Advanced Default Values

Sometimes a simple value isn’t enough, and you need more control over how a field behaves.

I often use the field() function from the dataclasses module when I want to hide a field from the repr or exclude it from comparisons.

For instance, if you are tracking employee data for a New York-based firm, you might want to exclude a sensitive ID from the printed output.

from dataclasses import dataclass, field

@dataclass
class NYCEmployee:
    name: str
    department: str
    salary: float
    employee_id: str = field(default="PENDING", repr=False)

emp = NYCEmployee("Alice Smith", "FinTech", 125000.0, "NYC-9982")
print(emp)  # The employee_id won't show up in the print statement

This level of granularity is what makes dataclasses so much more powerful than named tuples or standard classes for data-heavy tasks.

Handle Mutable Default Values with default_factory

A common mistake I see junior developers make is trying to use a list or a dictionary as a direct default value.

In Python, mutable defaults are shared across all instances, which can lead to disastrous data leaks between objects.

Dataclasses solve this elegantly with the default_factory argument inside the field() function.

Imagine you are building a system to track sports stats for the NFL. You want each player to have a list of weekly scores.

from dataclasses import dataclass, field
from typing import List

@dataclass
class NFLPlayerStats:
    player_name: str
    team: str
    # Incorrect: scores: List[int] = [] 
    # Correct way using default_factory:
    scores: List[int] = field(default_factory=list)

player1 = NFLPlayerStats("Patrick Mahomes", "Chiefs")
player1.scores.append(28)

player2 = NFLPlayerStats("Joe Burrow", "Bengals")
print(f"{player2.player_name} scores: {player2.scores}") # This will be an empty list, as expected

I executed the code above and added the screenshot below.

Default Values and Type Hints in Dataclasses Python

Using default_factory=list ensures that every new NFLPlayerStats object gets its own fresh list in memory.

Type Hinting for Optional and Union Fields

In real-world US business logic, data isn’t always simple. A field might be optional or accept multiple types.

I rely heavily on the typing module, specifically Optional and Union, to make my dataclasses robust.

If you’re processing tax forms like a W-2, certain fields might be missing depending on the filing status.

from dataclasses import dataclass
from typing import Optional, Union

@dataclass
class TaxFiling:
    taxpayer_id: str
    total_income: float
    # Middle name might not exist
    middle_name: Optional[str] = None
    # Deduction could be a flat amount (float) or a category code (str)
    deduction: Union[float, str] = 0.0

filing = TaxFiling(taxpayer_id="123-45-6789", total_income=85000.50)
print(filing)

This approach helps static type checkers like Mypy catch errors before you even run your code, saving you hours of debugging.

Enforce Type Hints at Runtime

It is important to remember that Python’s type hints are mostly for documentation and static analysis.

By default, dataclasses won’t stop you from passing a string into an integer field during runtime.

If I’m working on a critical financial application, say, for a Wall Street trading platform, I often add a __post_init__ method.

This method runs automatically after the __init__ is finished, allowing you to validate data types.

@dataclass
class StockTrade:
    symbol: str
    shares: int
    price: float

    def __post_init__(self):
        if not isinstance(self.shares, int):
            raise TypeError(f"Shares must be an integer, got {type(self.shares)}")
        if self.shares <= 0:
            ValueError("Cannot trade zero or negative shares")

# This will trigger the validation
try:
    trade = StockTrade("AAPL", "Ten", 150.0)
except TypeError as e:
    print(f"Validation Error: {e}")

Work with Class-Level Variables

Sometimes you need a variable that is shared by all instances of a dataclass, but you don’t want it to be treated as a field.

In this case, I use the ClassVar type hint. This tells the dataclass decorator to ignore the variable during initialization.

This is perfect for constants like a state-specific sales tax rate in a retail application.

from dataclasses import dataclass
from typing import ClassVar

@dataclass
class CaliforniaRetailItem:
    item_name: str
    base_price: float
    # Shared constant for all items
    SALES_TAX: ClassVar[float] = 0.0725

    def get_total(self) -> float:
        return self.base_price * (1 + self.SALES_TAX)

item = CaliforniaRetailItem("Laptop", 1200.00)
print(f"Total price in CA: ${item.get_total():.2f}")

Compare Type Hints and Default Values in Dataclasses

When you’re designing your data structures, it’s helpful to see how these features interact.

Below is a quick reference table based on my experience with high-scale Python projects.

FeatureSyntaxBest Use Case
Basic Type Hintfield: typeMandatory fields (e.g., Name, ID).
Simple Defaultfield: type = valueConstant-like strings or numbers.
Mutable Defaultfield: type = field(default_factory=list)Lists, Sets, or Dicts.
Hidden Fieldfield: type = field(repr=False)Passwords or sensitive tokens.
Constantfield: ClassVar[type] = valueGlobal settings for the class.

Using these correctly will ensure your code follows the “Pythonic” way and remains maintainable as your project grows.

In this tutorial, I’ve covered the essential ways to use default values and type hints in Python dataclasses.

Whether you’re building a simple script or a complex enterprise application for the US market, these tools are indispensable.

The more you practice using these structures, the more natural they will feel in your daily coding workflow.

You may also like to read:

51 Python Programs

51 PYTHON PROGRAMS PDF FREE

Download a FREE PDF (112 Pages) Containing 51 Useful Python Programs.

pyython developer roadmap

Aspiring to be a Python developer?

Download a FREE PDF on how to become a Python developer.

Let’s be friends

Be the first to know about sales and special discounts.