ComparisonComparisons

namedtuple vs dataclass

namedtuple creates an immutable, lightweight tuple with named fields, while @dataclass creates an ordinary, mutable class with a generated __init__, __repr__, and __eq__.

On this page

Side-by-side comparison

#
  • Inherits from

    collections.namedtuple
    tuple
    @dataclass
    An ordinary class (object)
  • Fields can be changed after creation

    collections.namedtuple
    No
    @dataclass
    Yes (by default), frozen=True blocks it
  • Indexing and unpacking like a tuple

    collections.namedtuple
    Yes
  • A dictionary key or a set element

    collections.namedtuple
    Yes
    @dataclass
    Only with frozen=True
  • A default value that is a list

    collections.namedtuple
    Through defaults=[...] for the trailing fields
    @dataclass
    Through field(default_factory=list)
  • Typical use

    collections.namedtuple
    Lightweight, fixed data: a function result, a CSV row
    @dataclass
    Data that changes, custom methods, validation

Example

#
Python
from collections import namedtuple
from dataclasses import dataclass

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

@dataclass
class Student:
    name: str
    points: int = 0

point = Point(3, 4)
student = Student("Emma")
student.points += 40

print(point, point[0])
print(student)
Resultsaved result, you can check it
Point(x=3, y=4) 3
Student(name='Emma', points=40)

Definition and usage

#

Both constructs eliminate repetitive code for classes that mainly store data: you declare the fields once, and __init__, a readable __repr__, and __eq__ comparison are generated automatically.

They differ in what the resulting object actually is. namedtuple creates a class that inherits from tuple: the object is immutable, you can index it, unpack it, and use it as a dictionary key, but changing a field ends in an AttributeError. @dataclass creates an ordinary class: the object is mutable unless you add frozen=True, itโ€™s not a tuple, so you donโ€™t index it by number, but itโ€™s easy to add it methods, default values through field(default_factory=...), and validation in __post_init__.

Choose namedtuple when the data is simple, fixed, and meant to be lightweight, e.g. the result of a function returning several values, or a row from a CSV file. Choose @dataclass when the class needs to change while the program runs, needs its own methods, or needs default values like a list or a dictionary.

Syntax

#
Syntax
from collections import namedtuple
Name = namedtuple("Name", ["field1", "field2"])

from dataclasses import dataclass
@dataclass
class Name:
    field1: Type
    field2: Type

More examples

#
namedtuple is immutable
Python
from collections import namedtuple

Point = namedtuple("Point", ["x", "y"])
point = Point(3, 4)

try:
    point.x = 10
except AttributeError:
    print("The field can't be changed")

moved = point._replace(x=10)
print(moved)
Resultsaved result, you can check it
The field can't be changed
Point(x=10, y=4)
dataclass makes it easy to add methods and default values
Python
from dataclasses import dataclass, field

@dataclass
class Task:
    title: str
    tags: list = field(default_factory=list)

    def summary(self):
        return f"{self.title}, tags: {len(self.tags)}"

task = Task("Learning Python")
task.tags.append("python")
print(task.summary())
print(task)
Resultsaved result, you can check it
Learning Python, tags: 1
Task(title='Learning Python', tags=['python'])

Good practices

#
  • A namedtuple works well for returning several values from a function: you can unpack the result like a tuple or read it by field names.
  • If you need immutability but also methods and the newer type-annotated syntax, consider @dataclass(frozen=True) or typing.NamedTuple.
  • Always create fields with a mutable default value in @dataclass (a list, a dictionary) through field(default_factory=...), otherwise youโ€™ll get an error right at the class definition.

Related entries

#

Spotted a mistake or missing an example? Write to us.