Learnixo
Back to blog
AI Systemsintermediate

What is the difference between List and Tuple?

Compare Python lists and tuples: mutability, memory, hashability, unpacking, use cases in AI/ML code, and when to choose each.

Asma Hafeez KhanMay 16, 20265 min read
PythonListTupleMutabilityData Structures
Share:š•

The Core Difference: Mutability

A list is mutable — you can change it after creation. A tuple is immutable — once created, it cannot change.

Python
# List: mutable
drugs = ["warfarin", "aspirin", "metformin"]
drugs.append("lisinopril")   # OK
drugs[0] = "heparin"         # OK
drugs.remove("aspirin")      # OK

# Tuple: immutable
vitals = (120, 80, 98.6)     # systolic, diastolic, temp
vitals[0] = 130              # TypeError: 'tuple' object does not support item assignment
vitals.append(72)            # AttributeError: 'tuple' has no 'append'

Syntax

Python
# List: square brackets
medications = ["warfarin", "aspirin"]

# Tuple: parentheses (or just commas)
coordinates = (40.7128, -74.0060)   # lat, lon
coordinates = 40.7128, -74.0060     # Same tuple — parens optional

# Single-element tuple — the trailing comma is required
single = (42,)        # tuple
not_tuple = (42)      # Just the int 42, wrapped in parens
print(type(single))   # <class 'tuple'>
print(type(not_tuple))  # <class 'int'>

# Empty
empty_list  = []
empty_tuple = ()

Memory and Performance

Tuples are more memory-efficient than lists:

Python
import sys

list_data  = [1, 2, 3, 4, 5]
tuple_data = (1, 2, 3, 4, 5)

print(sys.getsizeof(list_data))    # 104 bytes (Python overhead for dynamic sizing)
print(sys.getsizeof(tuple_data))   # 80 bytes (fixed allocation)


# Tuple creation is also faster
import timeit

list_time  = timeit.timeit("[1, 2, 3, 4, 5]", number=10_000_000)
tuple_time = timeit.timeit("(1, 2, 3, 4, 5)", number=10_000_000)
print(f"List: {list_time:.2f}s | Tuple: {tuple_time:.2f}s")
# Tuple is roughly 5-10% faster — matters in tight loops

Hashability: Tuples as Dict Keys

Lists are not hashable — you cannot use them as dictionary keys. Tuples are hashable (if their contents are hashable):

Python
# Lists cannot be dict keys
interaction_db = {}
interaction_db[["warfarin", "aspirin"]] = "Major"   # TypeError: unhashable type 'list'

# Tuples can be dict keys
interaction_db = {}
interaction_db[("warfarin", "aspirin")] = "Major"   # Works
interaction_db[("metformin", "contrast")] = "Major"

key = ("warfarin", "aspirin")
print(interaction_db[key])   # "Major"

# Tuples in sets (same reason)
seen_pairs: set = set()
seen_pairs.add(("warfarin", "aspirin"))   # Works
seen_pairs.add(["warfarin", "aspirin"])   # TypeError

Unpacking

Both types support unpacking, but tuple unpacking is more idiomatic:

Python
# Tuple unpacking — natural for fixed-structure data
systolic, diastolic, temp = (120, 80, 98.6)
lat, lon = 40.7128, -74.0060

# Extended unpacking with *
first, *rest = [1, 2, 3, 4, 5]
print(first)   # 1
print(rest)    # [2, 3, 4, 5]

*beginning, last = [1, 2, 3, 4, 5]
print(beginning)  # [1, 2, 3, 4]
print(last)       # 5

first, *middle, last = [1, 2, 3, 4, 5]
print(middle)  # [2, 3, 4]

# Common in AI: unpacking return values
def train_step(batch) -> tuple[float, float]:
    return 0.342, 0.891   # loss, accuracy

loss, accuracy = train_step(batch)
print(f"Loss: {loss:.3f} | Accuracy: {accuracy:.3f}")


# Swap without a temp variable
a, b = 1, 2
a, b = b, a   # Tuple unpacking under the hood
print(a, b)   # 2 1

Converting Between Them

Python
medications = ["warfarin", "aspirin", "metformin"]

# List → Tuple (make immutable)
med_tuple = tuple(medications)

# Tuple → List (make mutable)
med_list = list(med_tuple)

# Common pattern: receive as tuple (immutable config), convert to list to modify
def process_drug_list(drugs: tuple[str, ...]) -> list[str]:
    """Process a fixed set of drugs, returning a filtered list."""
    working = list(drugs)   # Convert to list for mutation
    working = [d for d in working if d != "aspirin"]
    return working

When to Use Each

Python
# Use LIST when:
# - Content changes over time (building results)
batch_results: list[float] = []
for item in dataset:
    score = evaluate(item)
    batch_results.append(score)   # Growing list

# - Order-independent elements
active_drugs = ["warfarin", "metformin"]
active_drugs.append("lisinopril")
active_drugs.remove("warfarin")


# Use TUPLE when:
# - Data is fixed / shouldn't change (coordinates, RGB colors, settings)
ORIGIN = (0.0, 0.0)
API_VERSIONS = ("v1", "v2", "v3")

# - Multiple return values from a function
def get_patient_stats(patient_id: str) -> tuple[float, float, int]:
    return 2.4, 120.5, 67   # INR, weight_kg, age

inr, weight, age = get_patient_stats("P001")

# - Dict keys or set members
interaction_seen: set[tuple[str, str]] = set()
interaction_seen.add(("warfarin", "aspirin"))

# - As function arguments that shouldn't be modified
ALLOWED_ROUTES = ("PO", "IV", "IM", "SC", "SL")   # Immutable config

def validate_route(route: str) -> bool:
    return route in ALLOWED_ROUTES

Named Tuples: Best of Both Worlds

When you want tuple immutability but dict-like field access:

Python
from collections import namedtuple

# Classic namedtuple
PatientVitals = namedtuple("PatientVitals", ["systolic", "diastolic", "temp_f", "spo2"])
vitals = PatientVitals(systolic=120, diastolic=80, temp_f=98.6, spo2=98)

print(vitals.systolic)     # 120 — named access
print(vitals[0])           # 120 — still a tuple, index access works
print(vitals._asdict())    # OrderedDict with all fields


# Preferred: dataclass (covered separately) or typing.NamedTuple
from typing import NamedTuple

class DrugInteraction(NamedTuple):
    drug_a: str
    drug_b: str
    severity: str
    mechanism: str

interaction = DrugInteraction(
    drug_a="warfarin",
    drug_b="aspirin",
    severity="Major",
    mechanism="Additive anticoagulation and antiplatelet effects",
)

print(interaction.severity)    # "Major"
print(interaction)             # DrugInteraction(drug_a='warfarin', ...)

Summary

| Feature | list | tuple | |---|---|---| | Syntax | [a, b, c] | (a, b, c) | | Mutable | Yes | No | | Hashable | No | Yes (if contents hashable) | | Memory | More | Less | | Speed | Slightly slower to create | Slightly faster to create | | Dict key / set member | No | Yes | | Typical use | Growing collections | Fixed records, return values, dict keys |

Enjoyed this article?

Explore the AI Systems learning path for more.

Found this helpful?

Share:š•

Leave a comment

Have a question, correction, or just found this helpful? Leave a note below.