Creating Tuples
Tuples are ordered, immutable sequences. They are defined with parentheses (or just commas).
# 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.
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.
# 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.
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).
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
| Feature | Tuple | List |
|---|---|---|
| Syntax | (1, 2, 3) | [1, 2, 3] |
| Mutable | No | Yes |
| Hashable | Yes (if contents are hashable) | No |
| Dict key | Yes | No |
| Set member | Yes | No |
| Memory | Slightly less | Slightly more |
| Speed | Slightly faster | Slightly slower |
| Methods | count, index | append, insert, remove, sort... |
| Use case | Fixed record (coordinates, config, DB row) | Collection that grows/shrinks |
# 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