# Tuples, namedtuple and dataclasses — Data Structures in Python

Source: https://www.skillbyai.com/en/data-structures-python/s-tuples

> Choose between tuples, named tuples and dataclasses for records.

## Fixed-shape records

A **`tuple`** is an **immutable** sequence. Because it cannot change, a tuple of hashable values is itself **hashable**, so it can be a dictionary key or a set member (for example, `(row, col)` coordinates). Tuples are slightly smaller and faster to create than lists, support **unpacking** (`name, score = pair`) and are the natural return type for multiple values. For readable records, **`collections.namedtuple`** or **`typing.NamedTuple`** adds field names while staying a tuple (immutable, indexable, lightweight). **`dataclasses.dataclass`** generates `__init__`, `__repr__` and `__eq__` for regular classes; options include `frozen=True` (immutable and hashable), `order=True` (comparison methods), `slots=True` (Python 3.10+, less memory and faster attribute access) and `field(default_factory=list)` for mutable defaults. Use tuples for small, fixed groupings and keys, named tuples for lightweight immutable records, and dataclasses when you want methods, defaults, type hints and optional mutability. Libraries such as **attrs** and **Pydantic** (with validation) offer more features.

## Records three ways

Tuples as keys, NamedTuple and a slotted dataclass.

```python
from dataclasses import dataclass, field
from typing import NamedTuple

# tuple as a dictionary key
seat_prices = {(1, "A"): 1200, (1, "B"): 1200, (10, "C"): 800}
print(seat_prices[(1, "A")])                  # 1200

class Point(NamedTuple):
    lat: float
    lng: float

pune = Point(18.52, 73.86)
lat, lng = pune                               # still a tuple: unpacks
print(pune.lat, pune[1])                      # 18.52 73.86

@dataclass(slots=True)
class Order:
    order_id: str
    city: str
    items: list[str] = field(default_factory=list)   # safe mutable default
    paid: bool = False

    def add(self, sku: str) -> None:
        self.items.append(sku)

@dataclass(frozen=True, order=True)
class Version:
    major: int
    minor: int

o = Order("o-1", "Pune")
o.add("pen")
print(o)                                      # Order(order_id='o-1', city='Pune', items=['pen'], paid=False)
print(sorted([Version(1, 10), Version(1, 2)]))   # [Version(major=1, minor=2), Version(major=1, minor=10)]
print({Version(2, 0): "current"})             # frozen dataclasses are hashable
```

## A printed form versus a notebook

A tuple is a printed form: fixed fields that cannot be changed once filled in, which makes it safe to file under an index (a dict key). A dataclass is a notebook template with labelled pages that you can keep editing.

**Quiz:** Why can a tuple of strings be used as a dictionary key while a list cannot?

- [ ] Tuples are faster
- [x] Tuples are immutable and hashable; lists are mutable and unhashable
- [ ] Lists cannot hold strings
- [ ] Dictionaries only accept tuples

*Answer:* Tuples are immutable and hashable; lists are mutable and unhashable. Keys must be hashable, and their hash must not change.
