NamedTuple vs TypedDict vs Dataclass: Which to Choose?
Python gives you three popular ways to bundle related data together: NamedTuple, TypedDict, and dataclass. Each one solves a slightly different problem.
Think of it like choosing a container for your lunch. A rigid bento box keeps everything in its place and nothing moves (NamedTuple). A labeled lunchbox lets you swap items freely (dataclass). A paper bag with labels is the lightest option but offers less protection (TypedDict).
By the end of this tutorial, you'll know exactly which container to reach for in any situation. Let's start with the most lightweight option.
What Is a NamedTuple and When Should You Use It?
A regular tuple like ('Alice', 25, 'NYC') works fine, but what does the 25 mean? Age? Score? Zip code? You have to remember the position of every value.
A NamedTuple fixes this by giving each position a name. You get the clarity of a class with the efficiency of a tuple.
Notice that you can access fields by name (p.x) or by index (p[0]). A NamedTuple is still a tuple under the hood, so everything that works with tuples works here too.
You can give fields default values. Defaults must come after non-default fields, just like function arguments.
NamedTuples also support unpacking, just like regular tuples. This is handy when passing data to functions.
What Is a TypedDict and How Does It Work?
A TypedDict is a dictionary where each key has a specific type. It looks like a class definition, but at runtime it's just a plain dict.
TypedDicts are perfect when you're working with JSON data from APIs or config files. The data is already a dictionary, and you want to document what keys it should have.
Since a TypedDict is just a dict, you can modify its values freely. This is the big difference from NamedTuple, which is frozen.
You can also mark certain keys as optional using total=False. This means not every key needs to be present.
How Do Dataclasses Compare?
A dataclass is the most flexible of the three. It's a real class with attributes, methods, and optional immutability. Think of it as the Swiss army knife of structured data.
Dataclasses automatically generate __init__, __repr__, and __eq__ methods. You can also add your own methods, use inheritance, and control mutability with frozen=True.
What Are the Key Differences Between All Three?
| Here's a quick comparison to help you decide: | |||
|---|---|---|---|
| --- | --- | --- | --- |
| Mutable | No | Yes | Yes (default) |
| Methods | Limited | No | Yes |
| Inheritance | Limited | Yes | Yes |
| Memory | Low | Medium | Medium |
| Hashable | Yes | No | Optional |
| Unpacking | Yes | No | No |
| JSON-friendly | No | Yes | No |
from typing import NamedTuple
class Point(NamedTuple):
x: float
y: float
p = Point(1.0, 2.0)
# p.x = 5 # Error! Immutable
print(p, type(p))from dataclasses import dataclass
@dataclass
class Point:
x: float
y: float
p = Point(1.0, 2.0)
p.x = 5 # Works fine
print(p, type(p))Which One Should You Choose?
Use NamedTuple when your data should never change after creation. Coordinates, database rows, and configuration snapshots are good examples. NamedTuples are also hashable, so you can use them as dictionary keys or put them in sets.
Use TypedDict when you're working with dictionary-shaped data, especially JSON from APIs. If the data is already a dict and you just want to document its structure, TypedDict is the lightest touch.
Use a dataclass for everything else. When you need methods, validation, or mutable objects with nice defaults, dataclasses are the best choice.
Practice Exercises
Create a NamedTuple called Color with three integer fields: red, green, and blue. Then create a color with values 255, 128, 0 and print it.
Create a TypedDict called ApiResponse with three fields: status (str), code (int), and data (str). Create an instance with status 'success', code 200, and data 'Hello'. Print the value of the 'status' key.
Read the code carefully. What will be printed? Remember: NamedTuples are immutable like regular tuples. The try/except block catches any errors.
Given a dictionary data, create a NamedTuple class called Book with fields title (str), author (str), and pages (int). Convert the dictionary to a Book instance using ** unpacking, then print the result.
Write a function called describe_container that takes a string argument. If the argument is 'immutable', return 'NamedTuple'. If it's 'json', return 'TypedDict'. For anything else, return 'dataclass'. Print the result for each of these inputs: 'immutable', 'json', 'flexible'.