Skip to lesson
Python for JS Developers

Modern PythonLesson 1 of 3

Dataclasses

3 min

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

JavaScriptPythonNote
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

JavaScript
// 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
# 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 browser

Create these dataclasses:

1. @dataclass Config: host: str, port: int = 8080, debug: bool = False, tags: a list that defaults to empty (use field!) 2. @dataclass(frozen=True) Coordinate: lat: float, lng: float 3. Create a Config with host "localhost" and print it 4. Create two equal Coordinates and print whether they are equal (==)
Expected output
Config(host='localhost', port=8080, debug=False, tags=[])
True

Ctrl + Enter runCtrl + Shift + Enter checkTab indentEsc then Tab to leave the editorCtrl + / comment