Modern PythonLesson 1 of 3
Dataclasses
A dataclass is a class whose boilerplate is generated for you. You declare the fields with type annotations, add the @dataclass decorator, and Python writes __init__, __repr__, and __eq__ for you. Instead of 20 lines of constructor and comparison code, you write 4 lines.
If you come from TypeScript, the declaration looks like an interface, but there is a big difference: a TS interface disappears at compile time, while a dataclass is a real class that exists at runtime. The closest TypeScript equivalent is a class with constructor parameter properties, plus auto-generated equality and printing.
Dataclasses also solve the mutable default trap. You cannot write tags: list = [] (Python raises a ValueError to protect you). Instead use field(default_factory=list), which creates a fresh list for every instance.
For immutable data, use @dataclass(frozen=True). Assigning to a field then raises FrozenInstanceError, and because the instances cannot change, Python also makes them hashable, so they work as dict keys and in sets. Like Object.freeze, this is shallow: a frozen dataclass holding a list does not freeze the list itself.
Useful options: slots=True (3.10+) makes instances smaller and faster and blocks typos in attribute names, kw_only=True forces callers to use keyword arguments, and order=True generates <, <=, >, >= by comparing fields in order.
Quick reference
| JavaScript | Python | Note |
|---|---|---|
| class { constructor(a, b) { ... } } | @dataclass class X: a: int | __init__ is generated |
| Manual toString() | Auto __repr__ | Readable printing for free |
| Manual equals() | Auto __eq__ | Compares field values |
| Object.freeze(obj) | @dataclass(frozen=True) | Shallow immutability, hashable |
| TS interface (compile-time only) | @dataclass (real runtime class) | Has a constructor and methods |
| { ...user, age: 31 } | dataclasses.replace(user, age=31) | Copy with changes |
Side by side
// TypeScript
interface User {
name: string;
age: number;
admin?: boolean;
}
class User {
constructor(
public name: string,
public age: number,
public admin = false,
) {}
// still manual: toString, equals...
}# Python dataclasses
from dataclasses import dataclass, field, asdict, replace
@dataclass
class User:
name: str
age: int
admin: bool = False
# Generated: __init__, __repr__, __eq__
u = User("Alice", 30)
print(u) # User(name='Alice', age=30, admin=False)
u == User("Alice", 30) # True
older = replace(u, age=31)
@dataclass(frozen=True) # immutable + hashable
class Point:
x: float
y: float
@dataclass
class Team:
name: str
members: list[str] = field(default_factory=list)
user_dict = asdict(u) # {'name': 'Alice', 'age': 30, 'admin': False}Exercise
Real Python, running in your browserCreate these dataclasses:
Config(host='localhost', port=8080, debug=False, tags=[]) True
Ctrl + Enter runCtrl + Shift + Enter checkTab indentEsc then Tab to leave the editorCtrl + / comment