Why tuples can beat lists

Constants, copy elision, exact allocation, and record-like data

A tuple allocates exactly. A list overallocates. That extra capacity is the deliberate price of efficient append. CPython's `PyListObject` uses an overallocation formula of roughly 12.5% per resize, giving amortized O(1) append. `PyTupleObject` has no overallocation because tuples are immutable. The list struct stores a separate `ob_item` pointer to a dynamically allocated array of `PyObject*` pointers. The tuple struct embeds `ob_item[]` inline after the header. For 10 floats, a list container is about 16 bytes larger than a tuple. This gap grows with the number of elements. Tuple copy elision: `tuple(t)` returns the same object when `t` is already a tuple. Tuples are hashable only when all elements are hashable. <a href="/memory-container-comparison">See how tuples compare with list and array.array in real measurements</a>. <a href="/classes-data-builders">Use namedtuple and NamedTuple for typed records</a>.

Understand.
Visualize.
Master.

Python in Depth

An interactive engineering reference for Python internals

Quick note

Use tuple when fixed shape is the truth.

:)
Python version

Targets Python 3.10–3.14. Python 3.9 and below are End-of-Life.

TABLE OF CONTENTS
4.3Why tuples can beat lists

Constants, copy elision, exact allocation, and record-like data

List and tuple both hold references, but mutability changes allocation strategy, API surface, and what callers may assume.

Core answer

Use list when an ordered collection must grow, shrink, or be updated. Use tuple when fixed length and stable sequence semantics are part of the contract.

# [CURRENT - 3.10-3.14] Works on Python 3.10+
from dataclasses import dataclass
@dataclass(frozen=True, slots=True)
class Endpoint:
host: str
port: int
def deployment_targets(items: list[Endpoint]) -> tuple[Endpoint, ...]:
return tuple(items)
draft = [Endpoint("api-1", 443), Endpoint("api-2", 443)]
published = deployment_targets(draft)
draft.append(Endpoint("api-3", 443))
print(draft)
print(published)

Why this design exists

Lists optimize mutable sequence work. Tuples give fixed-size sequence behavior and tuple-specific uses such as multiple return values, record-like grouping, and hashability when all contained objects are hashable.

Mechanics and CPython internals

CPython lists over-allocate their reference array so appends usually avoid immediate reallocation. Tuples allocate a fixed reference array for their length. Both are shallow containers over object references, so the mutability of contained objects is a separate question.

# [CURRENT - 3.10-3.14] Works on Python 3.10+
from dataclasses import dataclass
from sys import getsizeof
@dataclass(frozen=True, slots=True)
class Footprint:
kind: str
bytes: int
def compare(values: list[int]) -> list[Footprint]:
return [
Footprint("list", getsizeof(values)),
Footprint("tuple", getsizeof(tuple(values))),
]
print(compare(list(range(1000))))

Complexity and tradeoffs

Indexing is O(1) for both. List append is amortized O(1), while tuple "append" means allocating another tuple, typically O(n). Tuples can communicate stability and reduce growth overhead, but they do not make nested state immutable.

Idiomatic patterns and refactoring

Refactor mutable temporary assembly into an immutable published result when downstream code should only consume.

# [CURRENT - 3.10-3.14] Works on Python 3.10+
from dataclasses import dataclass
@dataclass(frozen=True, slots=True)
class Column:
name: str
def build_columns(raw: list[str]) -> tuple[Column, ...]:
columns: list[Column] = []
for name in raw:
columns.append(Column(name.casefold()))
return tuple(columns)
schema = build_columns(["Order_ID", "Amount_Cents"])
print(schema)
print(hash(schema))

Common mistakes and edge cases

Do not use a tuple merely because a collection should not be reassigned; callers can still mutate contained lists or dataclass instances. Do not repeatedly concatenate tuples in a growth loop.

When to use / When NOT to use

Use tuple to publish fixed ordered data and list to build or mutate ordered data.

Do not choose tuple as a deep immutability guarantee unless the element graph also honors that guarantee.

Further reading

  • Official docs: sequence types
  • Official docs: tuple hashing and comparisons
  • CPython source: Objects/listobject.c
  • CPython source: Objects/tupleobject.c
BOARD NOTESContext
WHY NO BENCHMARK?

This topic is better taught with structure, semantics, and cross-references than with a synthetic chart.

Use tuple when fixed shape is the truth.

RELATED GUIDES
NEXT CHECKS
Contribute