Things on this page are fragmentary and immature notes/thoughts of the author. Please read with your own judgement!
Tips and Traps¶
Prefer
Dataclasstonamedtuplefor many reasons.A namedtuple is immutable while a dataclass can be both mutable (
frozen=Falsewhich 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¶
from dataclasses import dataclass
@dataclass
class Person:
name: str
age: int = 10Person("Ben")Person(name='Ben', age=10)p = Person("Ben", 34)
pPerson(name='Ben', age=34)str(p)"Person(name='Ben', age=34)"dataclass - immutable¶
Attributes of the class Person is mutable
since frozen=False (default).
p.age = 20
pPerson(name='Ben', age=20)@dataclass(frozen=True)
class PersonImmutable:
name: str
age: int = 10PersonImmutable("Ben")PersonImmutable(name='Ben', age=10)p = PersonImmutable("Ben", 30)
pPersonImmutable(name='Ben', age=30)Attribute of PersonImmutable is immutable since frozen=True.
p.age = 20
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).
from dataclasses import dataclass
@dataclass
class MyDataClass:
name: str = "Alice"
# Accessing via the class directly
print(MyDataClass.name)
# Accessing via an instance
obj = MyDataClass()
print(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.
When you write
name: str = "Alice", Python evaluates and stores"Alice"on theMyDataClassclass object itself. (In contrast, fields declared without default values, such asname: str, only exist as type annotations in__annotations__and do not create a class attribute.)The
@dataclassdecorator inspects the class annotations and attributes, automatically generating an__init__method based on them.When you instantiate the object (
obj = MyDataClass()), the generated__init__method assigns the default value toself.nameon the instance, creating an instance attribute that shadows the class attribute. Because it is an instance attribute, modifyingobj.nameon an instance does not alterMyDataClass.name, and reassigningMyDataClass.nameafter 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 withtyping.ClassVar(e.g.,species: ClassVar[str] = "Human"). Dataclasses intentionally ignoreClassVarattributes 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.
from dataclasses import dataclass, field
@dataclass
class MyDataClassWithList:
items: list = field(default_factory=list)
# Accessing via an instance works
obj = MyDataClassWithList()
print(obj.items)[]
# Accessing via the class directly raises an AttributeError
try:
print(MyDataClassWithList.items)
except AttributeError as e:
print(f"AttributeError: {e}")AttributeError: type object 'MyDataClassWithList' has no attribute 'items'
namedtuple¶
from collections import namedtuplePersonNT = namedtuple("PersonNT", ["name", "age"])p = PersonNT("Ben Du", 30)
pPersonNT(name='Ben Du', age=30)p[0]'Ben Du'p[1]30Objects of namedtuple as pandas DataFrame Data¶
import pandas as pdpd.DataFrame(data=[p])