A tuple is like a list, with one crucial difference: it cannot be changed after it’s created. We say tuples are immutable.
Use a tuple when you have a fixed group of values that belong together and shouldn’t change — coordinates, RGB colours, a row in a CSV.
Creating a tuple
point: tuple[int, int] = (3, 4)
rgb: tuple[int, int, int] = (255, 128, 0)
person: tuple[str, int, str] = ("Manikandan", 30, "admin")
empty: tuple[int, ...] = ()
single: tuple[int] = (42,) # the comma matters!
A tuple:
- Uses parentheses
( ). - Items are separated by commas.
- The type hint
tuple[T1, T2, ...]describes each position;tuple[T, ...]means “any length, all of type T”.
The single-item tuple needs a trailing comma.
(42)is just the number 42 in parentheses.(42,)is a tuple containing one item.
Accessing items
Same as lists:
point: tuple[int, int] = (3, 4)
print(point[0]) # 3
print(point[1]) # 4
print(point[-1]) # 4
Slicing also works:
data: tuple[int, ...] = (10, 20, 30, 40, 50)
print(data[1:4]) # (20, 30, 40) — note: a tuple, not a list
Tuples cannot be changed
point: tuple[int, int] = (3, 4)
point[0] = 10 # TypeError: 'tuple' object does not support item assignment
You can’t add, remove, or replace items. To “change” a tuple, build a new one:
point = (10, point[1])
print(point) # (10, 4)
Why use tuples?
Three good reasons:
- Safety. When you pass a tuple around, no one can sneak in and modify it.
- Speed. Tuples are slightly faster than lists for the same operations.
- Intent. Using a tuple says “this is a fixed group of values, not a collection that might grow”.
A list of records is often a list of tuples:
people: list[tuple[str, int]] = [
("Alice", 30),
("Bob", 25),
("Carol", 35),
]
Each (name, age) pair is one record. Reordering or extending the list of records is normal — but each individual record shouldn’t change shape.
Packing and unpacking
This is one of Python’s nicest features. You can assign multiple variables from a tuple in one line:
point: tuple[int, int] = (3, 4)
x, y = point
print(x, y) # 3 4
The variables on the left are matched to the values inside the tuple. This is called unpacking.
You can unpack any iterable — lists, strings, ranges:
a, b, c = [1, 2, 3]
first, second, third = "abc"
But the shapes must match:
x, y = (1, 2, 3) # ValueError: too many values to unpack
The * in unpacking
To capture “the rest”, use *:
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
This is the same * you saw in *args, used here at the receiving end.
Swapping variables
The shortest variable swap in any language:
a: int = 1
b: int = 2
a, b = b, a
print(a, b) # 2 1
Behind the scenes, Python packs b, a into a tuple and unpacks it into a, b.
Returning multiple values
Functions often return a tuple to give back several values:
def min_and_max(numbers: list[int]) -> tuple[int, int]:
return min(numbers), max(numbers)
smallest, largest = min_and_max([3, 1, 4, 1, 5, 9, 2, 6])
print(smallest, largest) # 1 9
You’ll see this in ML libraries all the time: train_loss, val_loss = train_step(...).
Named tuples (a teaser)
When a tuple’s positions start meaning specific things, a regular tuple gets awkward — you have to remember that position 2 is email and position 3 is role. The standard library has a fix:
from typing import NamedTuple
class User(NamedTuple):
name: str
age: int
role: str
user = User("Manikandan", 30, "admin")
print(user.name, user.age) # Manikandan 30
It’s a tuple with named fields. We’ll meet this again in Sections 12 (Classes) and 14 (Standard Library). For now, it’s enough to know it exists.
Tuples in the wild
You’ll meet tuples everywhere:
dict.items()returns tuples of(key, value)enumerate()returns tuples of(index, value)zip()(next section) returns tuples- ML models often return tuples of
(predictions, attention)or similar
What’s next
Tuples are for fixed records. Next, sets — for collections where uniqueness matters.