PythonTuples

Tuples

A tuple is an ordered collection of values, much like a list — but once created, it cannot be changed. That single difference, immutability, is what tuples are all about: it makes them safer to pass around, hashable enough to use as dictionary keys, and a natural fit for values that belong together and shouldn’t drift apart.

Creating a Tuple

You create a tuple with parentheses (or often no brackets at all) and commas separating the values.

Python
point = (3, 4)
colors = ("red", "green", "blue")
mixed = ("Ada", 36, True)

# Parentheses are optional when the context is unambiguous
also_a_tuple = 3, 4
print(also_a_tuple)  # (3, 4)

# Build a tuple from any iterable with tuple()
from_list = tuple([1, 2, 3])
print(from_list)  # (1, 2, 3)

empty = ()
print(empty)  # ()
The Single-Element Tuple Gotcha

Parentheses alone don’t make a tuple — it’s the comma that does. Writing (1) just gives you the integer 1 wrapped in redundant parentheses. To create a one-item tuple you must include a trailing comma.

Don't forget the trailing comma
`(1)` is the plain integer `1`. `(1,)` is a one-item tuple. This is one of the most common tuple mistakes, especially when a function expects a tuple argument.

Python
not_a_tuple = (1)
print(type(not_a_tuple))  # <class 'int'>

single_tuple = (1,)
print(type(single_tuple))  # <class 'tuple'>
print(single_tuple)        # (1,)

# The comma matters even without parentheses
also_single = 1,
print(type(also_single))   # <class 'tuple'>
Immutability: Why It Matters

Once a tuple is created, you cannot add, remove, or reassign any of its items. Attempting to do so raises a TypeError.

Python
point = (3, 4)

# point[0] = 99  # TypeError: 'tuple' object does not support item assignment
# point.append(5)  # AttributeError: 'tuple' object has no attribute 'append'

print(point[0])  # 3 - reading is fine, only mutation is blocked

Immutability brings a few concrete, practical benefits.

  • Hashability — because a tuple’s contents can never change, Python can compute a stable hash for it, which means a tuple (of hashable items) can be used as a dictionary key or stored in a set. A list cannot.

  • Safety — if you pass a tuple into a function, you can be certain the function did not silently mutate your data. Passing a list gives no such guarantee.

  • Intent — using a tuple signals to other readers of your code “this is a fixed-size, fixed-shape record,” while a list signals “this collection may grow or shrink.”

  • Performance — tuples are slightly more memory-efficient and faster to create than lists, since Python doesn’t need to reserve room for future growth.

Python
locations = {
    (40.7128, -74.0060): "New York",
    (51.5074, -0.1278): "London",
}
print(locations[(40.7128, -74.0060)])  # New York

# A list can't be used as a key:
# bad = {[1, 2]: "oops"}  # TypeError: unhashable type: 'list'
Tuple Packing and Unpacking

Writing several comma-separated values as one tuple is called packing. Pulling them back out into individual names is called unpacking — and it is one of the most useful tuple features in everyday Python.

Python
# Packing: several values collapse into one tuple
point = 3, 4, 5
print(point)  # (3, 4, 5)

# Unpacking: a tuple's values spread into separate names
x, y, z = point
print(x, y, z)  # 3 4 5

# Swapping two variables without a temp variable
a, b = 1, 2
a, b = b, a
print(a, b)  # 2 1

# Star-unpacking captures the "rest" into a list
first, *rest = (1, 2, 3, 4)
print(first, rest)  # 1 [2, 3, 4]

# Functions that return multiple values are really returning a tuple
def min_max(numbers):
    return min(numbers), max(numbers)

lo, hi = min_max([4, 1, 9, 3])
print(lo, hi)  # 1 9
Named Tuples

Plain tuples are accessed by position, which can get hard to read once you have more than two or three fields — person[1] doesn’t tell you much on its own. Named tuples solve this by letting you access fields by name while keeping all the performance and immutability benefits of a regular tuple.

The standard library offers two ways to create one: collections.namedtuple, a factory function, and typing.NamedTuple, a more modern class-based syntax with type hints.

Python
from collections import namedtuple

Point = namedtuple("Point", ["x", "y"])
p = Point(3, 4)

print(p.x, p.y)   # 3 4
print(p[0], p[1]) # 3 4  - still works like a regular tuple
print(p)          # Point(x=3, y=4)

x, y = p          # unpacking still works too
print(x, y)       # 3 4

Python
from typing import NamedTuple

class Point(NamedTuple):
    x: int
    y: int

p = Point(3, 4)
print(p.x, p.y)  # 3 4
print(p)         # Point(x=3, y=4)

# Still an immutable tuple under the hood
# p.x = 10  # AttributeError: can't set attribute
Tip
Prefer `typing.NamedTuple` in new code: it reads like a normal class definition, supports type hints and default values, and plays nicely with editors and static type checkers. Use `collections.namedtuple` when you need to build the type dynamically from a list of field names at runtime, or when supporting very old codebases.
Tuple vs List: When to Use Which

Situation

Prefer

Collection will grow or shrink over its lifetime

List

Fixed-size “record” whose fields won’t change (e.g. a coordinate)

Tuple

Needs to be used as a dict key or stored in a set

Tuple

Returning multiple values from a function

Tuple

You want to guarantee callers can’t mutate the data

Tuple

Items need sort(), append(), remove(), etc.

List

Homogeneous collection of similar items (usernames, scores)

List

Heterogeneous, positional fields (name, age, email)

Tuple (ideally a named tuple)

Note
A useful rule of thumb: if you find yourself describing a tuple’s slots with names in a comment (“index 0 is the id, index 1 is the name”), that’s a strong signal to reach for a named tuple, a `dataclass`, or a small class instead.