Records#

Fixed-shape data with named fields. Three options, in increasing weight: namedtuple for compact immutable rows, @dataclass for any record with behaviour or mutation, TypedDict when the record is really a JSON object the operator is annotating.

namedtuple#

Lightweight tuple subclass with named fields. Immutable. Compact memory, fast access. Use for return values and simple records.

from collections import namedtuple

Point = namedtuple("Point", "x y")
p = Point(3, 4)
p.x, p.y, p[0], p[1]
p._replace(x=10)              # returns a new Point

dataclass#

Mutable by default, frozen with frozen=True. Supports defaults, type hints, __post_init__, slots, comparison methods.

from dataclasses import dataclass, field

@dataclass(frozen=True, slots=True)
class Target:
    host: str
    port: int = 443
    tags: tuple[str, ...] = ()

frozen=True makes the instance hashable and immutable; slots=True drops the per-instance __dict__ and shaves memory.

TypedDict#

A dict subclass with statically declared key types. Use when the data structure is a JSON object and a real class would be over-engineering.

from typing import TypedDict

class Scan(TypedDict):
    host: str
    port: int
    open: bool

s: Scan = {"host": "example.com", "port": 443, "open": True}

References#