Python Tuples

Understand Python tuples: immutability, tuple unpacking, named tuples, and when to choose tuples over lists.

Beginner 8 min read 8 examples

Creating Tuples

Tuples are ordered, immutable sequences. They are defined with parentheses (or just commas).

Python
# Empty tuple
empty = ()
empty2 = tuple()

# Tuple with values
coords   = (10, 20)
rgb      = (255, 128, 0)
person   = ("Alice", 30, "Engineer")
mixed    = (1, "hello", 3.14, True, None)

# Parentheses are optional - commas make a tuple
point = 3, 4            # same as (3, 4)
triple = 1, 2, 3        # same as (1, 2, 3)
print(type(triple))     # 

# Single-element tuple - MUST have trailing comma
single = (42,)          # tuple
not_tuple = (42)        # this is just 42 (integer)
print(type(single))     # 
print(type(not_tuple))  # 

# From other iterables
from_list   = tuple([1, 2, 3])
from_range  = tuple(range(5))       # (0, 1, 2, 3, 4)
from_string = tuple("abc")          # ('a', 'b', 'c')

# Indexing and slicing (same as lists)
t = (10, 20, 30, 40, 50)
print(t[0])     # 10
print(t[-1])    # 50
print(t[1:4])   # (20, 30, 40)  - slice returns a new tuple
print(len(t))   # 5

# Membership
print(30 in t)      # True
print(99 not in t)  # True

# Iteration
for item in (1, 2, 3):
    print(item)

Immutability

Tuples cannot be modified after creation. This makes them safe to use as dictionary keys and in sets.

Python
t = (1, 2, 3)

# Cannot modify items
# t[0] = 99           # TypeError: 'tuple' object does not support item assignment
# t.append(4)         # AttributeError: 'tuple' object has no attribute 'append'
# del t[0]            # TypeError

# Tuples are hashable (usable as dict keys and set members)
location = (40.7128, -74.0060)   # NYC coordinates
distances = {location: 0}        # valid dict key
point_set = {(0, 0), (1, 0), (0, 1)}  # valid set members

# Lists are NOT hashable
my_list = [1, 2, 3]
# d = {my_list: "value"}  # TypeError: unhashable type: 'list'

# Tuple containing mutable object - the tuple is still immutable
# but the inner list can be changed
t2 = ([1, 2], [3, 4])
t2[0].append(99)        # modifies the inner list - OK
print(t2)               # ([1, 2, 99], [3, 4])
# t2[0] = [1, 2, 99]   # TypeError - cannot rebind element

# Concatenation creates a new tuple
a = (1, 2)
b = (3, 4)
c = a + b           # (1, 2, 3, 4)  - new tuple
d = a * 3           # (1, 2, 1, 2, 1, 2) - new tuple

print(a)            # (1, 2)  - unchanged

Tuple Unpacking

Tuple unpacking lets you assign multiple variables in a single statement. It works with any iterable, not just tuples.

Python
# Basic unpacking - number of variables must match
x, y = (10, 20)
print(x, y)         # 10 20

# Parentheses are optional
a, b, c = 1, 2, 3
print(a, b, c)      # 1 2 3

# Swap variables (classic Pythonic idiom)
a, b = 10, 20
a, b = b, a         # no temporary variable needed
print(a, b)         # 20 10

# Star unpacking (*) - capture remainder
first, *rest = (1, 2, 3, 4, 5)
print(first)    # 1
print(rest)     # [2, 3, 4, 5]  (always a list)

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

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

# Ignore values with _ (convention for "don't care")
_, y_coord = (10, 20)   # only need y
print(y_coord)          # 20

first, _, third, *_ = (1, 2, 3, 4, 5, 6)
print(first, third)     # 1 3

# Unpacking in for loops
points = [(1, 2), (3, 4), (5, 6)]
for x, y in points:
    print(f"x={x}, y={y}")
# x=1, y=2
# x=3, y=4
# x=5, y=6

# Enumerate unpacking
fruits = ["apple", "banana", "cherry"]
for index, fruit in enumerate(fruits):
    print(f"{index}: {fruit}")

# Function returning tuple - unpack at call site
def min_max(nums):
    return min(nums), max(nums)   # returns a tuple

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

Named Tuples

Named tuples add field names to a tuple, making code self-documenting without the overhead of a class.

Python
from collections import namedtuple

# Define a named tuple type
Point = namedtuple("Point", ["x", "y"])
Color = namedtuple("Color", ["red", "green", "blue"])

# Create instances
p = Point(10, 20)
c = Color(255, 128, 0)

# Access by name OR by index
print(p.x, p.y)         # 10 20
print(p[0], p[1])       # 10 20  - still works as regular tuple

# Named tuples are immutable
# p.x = 99   # AttributeError

# Unpack like a regular tuple
x, y = p
print(x, y)     # 10 20

# Useful methods
print(p._fields)        # ('x', 'y')
print(p._asdict())      # {'x': 10, 'y': 20}  (OrderedDict)

# _replace() creates a new tuple with some fields changed
p2 = p._replace(x=50)
print(p2)           # Point(x=50, y=20)
print(p)            # Point(x=10, y=20)  - original unchanged

# Named tuples work with all tuple operations
distance = sum(coord**2 for coord in p) ** 0.5
print(f"Distance from origin: {distance:.2f}")

# Python 3.6+ alternative with typing.NamedTuple (recommended for new code)
from typing import NamedTuple

class Employee(NamedTuple):
    name: str
    department: str
    salary: float = 50000.0     # default value

emp = Employee("Alice", "Engineering", 90000.0)
print(emp.name)         # Alice
print(emp.department)   # Engineering

emp2 = Employee("Bob", "HR")    # uses default salary
print(emp2.salary)      # 50000.0

# Sorting a list of named tuples
employees = [
    Employee("Charlie", "Engineering", 85000),
    Employee("Alice", "HR", 70000),
    Employee("Bob", "Engineering", 95000),
]
by_salary = sorted(employees, key=lambda e: e.salary)
for e in by_salary:
    print(f"{e.name}: ${e.salary:,}")

Tuple Methods

Tuples have only two built-in methods (because they are immutable).

Python
t = (1, 2, 3, 2, 4, 2, 5)

# count() - number of occurrences
print(t.count(2))   # 3

# index() - position of first occurrence
print(t.index(3))   # 2
print(t.index(2))   # 1  (first occurrence)
# t.index(99)       # ValueError if not found

# Built-in functions work on tuples
print(len(t))       # 7
print(min(t))       # 1
print(max(t))       # 5
print(sum(t))       # 19
print(sorted(t))    # [1, 2, 2, 2, 3, 4, 5] - returns a list
print(reversed(t))  # iterator - wrap in tuple() or list()

# Convert between tuple and list when you need to mutate
t2 = (1, 2, 3)
lst = list(t2)
lst.append(4)
t3 = tuple(lst)     # (1, 2, 3, 4)
print(t3)

Tuples vs Lists

FeatureTupleList
Syntax(1, 2, 3)[1, 2, 3]
MutableNoYes
HashableYes (if contents are hashable)No
Dict keyYesNo
Set memberYesNo
MemorySlightly lessSlightly more
SpeedSlightly fasterSlightly slower
Methodscount, indexappend, insert, remove, sort...
Use caseFixed record (coordinates, config, DB row)Collection that grows/shrinks
Python
# USE TUPLES for:
# 1. Fixed records - data that represents one thing
point   = (3.0, 4.0)            # x, y coordinate
color   = (255, 128, 0)         # RGB
db_row  = ("Alice", 30, "NYC")  # one database record

# 2. Multiple return values from a function
def divmod_custom(a, b):
    return a // b, a % b    # returns tuple implicitly

quotient, remainder = divmod_custom(17, 5)

# 3. Dictionary keys (tuples of hashable items)
grid = {}
grid[(0, 0)] = "start"
grid[(3, 4)] = "finish"

# 4. Unpacking
lat, lng = (51.5074, -0.1278)   # London

# USE LISTS for:
# 1. Collections of similar items that may change
todo = ["buy milk", "write code", "exercise"]
todo.append("read book")

# 2. Building sequences incrementally
results = []
for i in range(10):
    results.append(i * i)

# 3. When you need list methods (sort, remove, etc.)
scores = [85, 92, 78, 95, 88]
scores.sort(reverse=True)       # sort in place

Frequently Asked Questions

They serve different semantic purposes. Lists represent homogeneous sequences of items that may change over time (a list of users, a queue of tasks). Tuples represent a fixed record of heterogeneous data (coordinates, RGB color, database row). Tuples signal to the reader "this structure will not change." They are also slightly faster to create and iterate, hashable (so usable as dict keys), and protect against accidental mutation.

Add a trailing comma: t = (42,) or just t = 42,. Without the comma, (42) is just the integer 42 in parentheses - not a tuple. The comma is what makes a tuple, not the parentheses. You can verify with type((42,)) - it returns <class 'tuple'>.

Yes. A tuple's immutability means you cannot reassign its elements - you cannot do t[0] = x. But if an element is itself a mutable object like a list, you can modify the contents of that list: t = ([1, 2], 3); t[0].append(4) works fine because you are not changing what t[0] refers to, just the list it refers to. However, such a tuple is no longer hashable.

A named tuple lets you access fields by name instead of position: point.x instead of point[0]. Use it when a plain tuple's fields would be unclear without context (a 3-tuple for RGB is obvious, but a 7-tuple for a database record is not). Named tuples are lightweight - they have no per-instance dictionary overhead. For more complex cases with default values and methods, prefer @dataclass (Python 3.7+).