Chapter 7 of 21
Functions & Lambdas
Current Study Module
Lists & Tuples
Dictionaries & Sets
Chapter 06 • Sequence Data Structures

Lists & Tuples: Memory Layout, Slicing & Performance

Lists and tuples are the foundational sequential data structures in Python. Learn how CPython implements lists as dynamic arrays with amortized O(1) growth, why immutable tuples use less memory and support hash keys, and how to harness advanced slicing and structured unpacking.

1. How CPython Implements Lists (Dynamic Over-Allocation)

In CPython, a list is not a linked list; it is a contiguous dynamic array of pointersto other PyObject instances on the heap. When appending elements, resizing the array on every insertion would result in terrible O(N^2) performance.

Instead, CPython uses an over-allocation formula (approx. new_allocated = (size + (size >> 3) + 6) & ~3). This ensures that list.append() achieves an amortized O(1) constant time complexity!

import sys

# Observe dynamic over-allocation in real time
items = []
print(f"Empty list initial bytes: {sys.getsizeof(items)}")

for i in range(12):
    items.append(i)
    # Notice bytes jump in discrete chunks (over-allocation) rather than per element!
    print(f"Length: {len(items):2d} | Allocated Memory: {sys.getsizeof(items):3d} bytes")

2. List vs Tuple: Architectural Differences

MetricPython List (list)Python Tuple (tuple)
MutabilityMutable (can append, remove, sort in-place)Immutable (fixed size and contents at creation)
Memory FootprintLarger (includes over-allocated spare slots)Compact (exact memory allocated, zero spare slots)
HashabilityUnindexable (cannot be a dict key or set element)Hashable (can be used as a dictionary key if contents are hashable)
Primary PurposeHomogeneous collections of dynamic itemsHeterogeneous fixed records (e.g. coordinates (x, y, z))

3. Advanced Slicing Syntax: [start:stop:step]

Slicing allows you to extract sub-sequences cleanly. Slices are half-open intervalsincluding start up to but excluding stop:

data = [0, 10, 20, 30, 40, 50, 60, 70, 80, 90]

print(data[2:6])    # [20, 30, 40, 50] (Elements from index 2 to 5)
print(data[::2])    # [0, 20, 40, 60, 80] (Every 2nd element)
print(data[::-1])   # [90, 80, 70, 60, 50, 40, 30, 20, 10, 0] (Reverse list)
print(data[-3:])    # [70, 80, 90] (Last 3 elements)

4. Self-Documenting Data Records with typing.NamedTuple

Instead of accessing tuple elements using error-prone integer indices (record[0], record[1]), use typing.NamedTuple to create memory-efficient, self-documenting data classes:

from typing import NamedTuple

class GeoCoordinate(NamedTuple):
    latitude: float
    longitude: float
    elevation_m: float = 0.0

coord = GeoCoordinate(latitude=37.7749, longitude=-122.4194)

# Access by readable attribute name OR by index!
print("Latitude:", coord.latitude)
print("Index 0:", coord[0])

# Unpacking works seamlessly
lat, lon, elev = coord
print(f"Unpacked location: {lat}, {lon}")

Explore Related Tools

Hand-picked utilities and calculators related to this tool.

Related Tools