Back to Blog
Python

python dataclass frozen vs namedtuple: Which to Choose?

python dataclass frozen vs namedtuple: Compare Python frozen dataclasses and namedtuples: syntax, immutability, type hints, defaults, methods, and when to choose each...

dataclassesnamedtupleimmutabilitytype hints
A visual comparison of Python frozen dataclass and namedtuple data containers showing immutability and structure.

The Core Decision: Frozen Dataclass or Namedtuple?

When you need an immutable data container in Python, the choice often comes down to python dataclass frozen vs namedtuple. Both give you a lightweight object with named fields, but they differ in syntax, type handling, and runtime behavior. This article compares them directly so you can pick the right one for your codebase.

Syntax and Declaration Differences

A namedtuple is created with a factory function:

from collections import namedtuple Point = namedtuple("Point", ["x", "y"]) p = Point(1, 2)

A frozen dataclass uses a class definition with the @dataclass(frozen=True) decorator:

from dataclasses import dataclass @dataclass(frozen=True) class Point: x: int y: int p = Point(1, 2)

The dataclass version is more explicit about types and reads like a regular class. The namedtuple is more concise but relies on string field names.

Immutability Behavior and Nested Mutation

Both types prevent reassigning attributes. Attempting p.x = 3 raises AttributeError for both. However, immutability is shallow. If a field holds a mutable object like a list, you can still modify that list:

@dataclass(frozen=True) class Container: items: list c = Container([1, 2, 3]) c.items.append(4) # works

The same happens with namedtuple. Neither provides deep immutability. If you need that, you must store immutable types or use a custom implementation.

Type Hints and Field Definitions

Dataclasses require type annotations for each field. This gives you static type checking with tools like mypy and better editor autocompletion. Namedtuples do not enforce types; they are just tuples with named access. You can add type hints to a namedtuple using typing.NamedTuple, but that is a separate class-based syntax:

from typing import NamedTuple class Point(NamedTuple): x: int y: int

This is closer to a dataclass but still lacks features like field metadata and __post_init__.

Default Values and Field Options

Dataclasses support default values, default_factory for mutable defaults, and field() for metadata:

from dataclasses import dataclass, field @dataclass(frozen=True) class Config: name: str retries: int = 3 tags: list = field(default_factory=list)

Namedtuples support default values from Python 3.7, but only for the trailing fields, and they cannot use a factory:

Point = namedtuple("Point", ["x", "y"], defaults=[0])

If you need per-field metadata or complex default logic, a dataclass is more flexible.

Methods and Extensibility

Dataclasses are ordinary classes, so you can define methods directly:

@dataclass(frozen=True) class Rectangle: width: float height: float def area(self) -> float: return self.width * self.height

Namedtuples are tuple subclasses. You can add methods by subclassing, but the syntax is more awkward and you must override __new__ if you want custom initialization. Dataclasses also support __post_init__ for validation, which namedtuples lack.

Performance and Memory Considerations

Namedtuples are built on tuples, so they are extremely memory efficient and fast to create. Dataclasses, by default, store each instance in a __dict__, which uses more memory and has slower attribute access. You can mitigate this by adding slots=True to the dataclass decorator:

@dataclass(frozen=True, slots=True) class Point: x: int y: int

This removes __dict__ and reduces memory usage, bringing it closer to a namedtuple. However, slots have their own limitations, such as no __dict__ for dynamic attributes. For high-frequency object creation in tight loops, namedtuple may still be faster, but the difference is often negligible unless you measure it in your specific workload.

When to Use Which

Choose a frozen dataclass when you need type hints, field defaults, validation, or methods. It is the better fit for domain models and configuration objects where readability and maintainability matter more than raw performance.

Choose a namedtuple when you need a simple, lightweight immutable record with minimal ceremony. It is ideal for internal data passing, return values from functions, or when you want tuple unpacking and comparison behavior out of the box.

If you are working with a codebase that already uses dataclasses, adding a frozen dataclass is more consistent. If you are optimizing memory for millions of small objects, a namedtuple or a slots-based dataclass is worth considering.

python dataclass frozen vs namedtuple: Which to Choose? | RYUSLOG DEV