Skip to article frontmatterSkip to article content
Site not loading correctly?

This may be due to an incorrect BASE_URL configuration. See the MyST Documentation for reference.

Dataclass vs namedtuple in Python

Things on this page are fragmentary and immature notes/thoughts of the author. Please read with your own judgement!

Tips and Traps

  1. Prefer Dataclass to namedtuple for many reasons.

    • A namedtuple is immutable while a dataclass can be both mutable (frozen=False which is the default) or immutable (frozen=True).

    However, namedtuple does have one advantage over dataclass. Members of a namedtuple is assible both via the dot operator and index. In situations where both dot accessing and index accessing of members is required, a namedtuple comes handy. For examples, a list of namedtuple objects can be used as the data for creating a pandas DataFrame but not a list of dataclass objects.

dataclass - mutable

Person(name='Ben', age=10)
Person(name='Ben', age=34)
"Person(name='Ben', age=34)"

dataclass - immutable

Attributes of the class Person is mutable since frozen=False (default).

Person(name='Ben', age=20)
PersonImmutable(name='Ben', age=10)
PersonImmutable(name='Ben', age=30)

Attribute of PersonImmutable is immutable since frozen=True.


FrozenInstanceErrorTraceback (most recent call last)
<ipython-input-22-b29e99bf1482> in <module>
----> 1 p.age = 20

<string> in __setattr__(self, name, value)

FrozenInstanceError: cannot assign to field 'age'

Dataclass Members with Default Values

In Python, fields of a dataclass instance are accessed via the dot operator (e.g., obj.member). When a field is defined with a default value (e.g., name: str = "Alice"), it also exists as a class variable (class attribute) on the dataclass itself.

Consequently, you can reference it directly via the class (MyDataClass.name) as well as via an instance (obj.name).

Alice
Alice

How It Works Under the Hood

When you define a class in Python, anything assigned directly within the class body becomes a class attribute.

  1. When you write name: str = "Alice", Python evaluates and stores "Alice" on the MyDataClass class object itself. (In contrast, fields declared without default values, such as name: str, only exist as type annotations in __annotations__ and do not create a class attribute.)

  2. The @dataclass decorator inspects the class annotations and attributes, automatically generating an __init__ method based on them.

  3. When you instantiate the object (obj = MyDataClass()), the generated __init__ method assigns the default value to self.name on the instance, creating an instance attribute that shadows the class attribute. Because it is an instance attribute, modifying obj.name on an instance does not alter MyDataClass.name, and reassigning MyDataClass.name after creation does not affect existing instances.

Note: If you want a true shared class variable that should not be treated as an instance field or included in __init__, annotate it with typing.ClassVar (e.g., species: ClassVar[str] = "Human"). Dataclasses intentionally ignore ClassVar attributes during code generation.

The Exception: default_factory for Mutable Defaults

There is one major scenario where a default value is not available as a class variable: when using default_factory.

Because sharing mutable defaults (such as lists or dictionaries) across all instances of a class causes subtle bugs in Python, dataclasses forbid direct mutable defaults (e.g., items: list = [] raises a ValueError) and require field(default_factory=...) instead.

When you use a factory, the default value is generated dynamically during instantiation inside __init__, so it is never assigned to the class itself. Attempting to access it directly via the class raises an AttributeError.

[]
AttributeError: type object 'MyDataClassWithList' has no attribute 'items'

namedtuple

PersonNT(name='Ben Du', age=30)
'Ben Du'
30

Objects of namedtuple as pandas DataFrame Data

Loading...