🎯 Learning Objectives
- Create tuples and understand their immutability
- Use tuple packing, unpacking, and named tuples
- Know when to choose a tuple over a list
- Create sets and understand uniqueness & unordered nature
- Perform set operations: union, intersection, difference, symmetric difference
- Use
frozensetfor immutable sets
Tuples
A tuple is an ordered, immutable sequence. Once created, you cannot add, remove, or change its elements.
# Creating tuples
point = (3, 4)
rgb = (255, 128, 0)
single = (42,) # trailing comma required for single-element tuple!
empty = ()
mixed = ("hello", 3.14, True)
# Parentheses are optional (it's the commas that make a tuple)
coords = 10, 20, 30
print(type(coords)) # <class 'tuple'>
# From other iterables
t = tuple([1, 2, 3]) # from list
t2 = tuple("hello") # ('h', 'e', 'l', 'l', 'o')
create_tuples.py
(42) is just
the integer 42. You need a trailing comma: (42,).
Indexing & Slicing
Same rules as lists — zero-based, negative indices, slicing returns a new tuple:
colors = ("red", "green", "blue", "yellow")
print(colors[0]) # "red"
print(colors[-1]) # "yellow"
print(colors[1:3]) # ("green", "blue")
print(len(colors)) # 4
print("red" in colors) # True
tuple_access.py
Immutability
t = (1, 2, 3)
# These all raise TypeError:
# t[0] = 99
# t.append(4)
# del t[1]
# But! If a tuple CONTAINS a mutable object, that object can change:
tricky = ([1, 2], [3, 4])
tricky[0].append(99)
print(tricky) # ([1, 2, 99], [3, 4]) — the list inside changed!
immutability.py
Tuple Packing & Unpacking
# Packing — assigning multiple values creates a tuple
coordinates = 4, 5, 6
# Unpacking — assign tuple elements to separate variables
x, y, z = coordinates
print(x, y, z) # 4 5 6
# Swap values (uses tuple packing/unpacking under the hood)
a, b = 1, 2
a, b = b, a
print(a, b) # 2 1
# Star unpacking
first, *rest = (1, 2, 3, 4, 5)
print(first) # 1
print(rest) # [2, 3, 4, 5] ← note: rest is a list
# Ignore values with _
_, y, _ = (10, 20, 30)
print(y) # 20
# Functions can return multiple values as a tuple
def min_max(numbers):
return min(numbers), max(numbers)
lo, hi = min_max([4, 8, 1, 9])
print(lo, hi) # 1 9
unpacking.py
x, y = get_coords().
Named Tuples
When tuple positions have specific meanings, use namedtuple for self-documenting code:
from collections import namedtuple
# Define a named tuple type
Point = namedtuple("Point", ["x", "y"])
Color = namedtuple("Color", "r g b")
# Create instances
p = Point(3, 4)
c = Color(255, 128, 0)
# Access by name (much clearer than index)
print(p.x, p.y) # 3 4
print(c.r, c.g, c.b) # 255 128 0
# Still works like a regular tuple
print(p[0]) # 3
x, y = p # unpacking works
print(len(c)) # 3
named_tuples.py
When to Use a Tuple vs a List
| Use a Tuple when… | Use a List when… |
|---|---|
| Data shouldn't change (coordinates, RGB, config) | Data will grow/shrink (shopping cart, logs) |
| You need a hashable type (dict keys, set elements) | Order matters and you need mutation |
| Returning multiple values from a function | Collecting items in a loop |
| You want to signal "this is fixed" | You want to signal "this will change" |
Sets
A set is an unordered collection of unique elements. Duplicates are automatically removed.
# Creating sets
fruits = {"apple", "banana", "cherry"}
numbers = {1, 2, 3, 2, 1} # duplicates removed
print(numbers) # {1, 2, 3}
# Empty set — must use set(), NOT {} (that's an empty dict!)
empty = set()
# From other iterables
from_list = set([1, 2, 2, 3, 3, 3]) # {1, 2, 3}
from_string = set("hello") # {'h', 'e', 'l', 'o'}
# Sets can only contain HASHABLE (immutable) items
valid = {1, "hi", (1, 2), True}
# invalid = {[1, 2]} ← TypeError: unhashable type: 'list'
create_sets.py
Modifying Sets
s = {1, 2, 3}
# Add a single element
s.add(4) # {1, 2, 3, 4}
s.add(2) # no effect — already present
# Remove elements
s.remove(3) # {1, 2, 4} — raises KeyError if missing
s.discard(99) # no error if missing
popped = s.pop() # removes & returns an arbitrary element
s.clear() # empty set
# Add multiple elements
s = {1, 2}
s.update([3, 4, 5]) # {1, 2, 3, 4, 5}
s.update("abc") # adds 'a', 'b', 'c'
set_methods.py
.discard() over .remove() when you're not sure the
element exists — it silently does nothing instead of raising an error.
Set Operations
Sets support powerful mathematical operations:
| Operation | Operator | Method | Result |
|---|---|---|---|
| Union | a | b | a.union(b) | All elements from both |
| Intersection | a & b | a.intersection(b) | Elements in both |
| Difference | a - b | a.difference(b) | In a but not in b |
| Symmetric diff | a ^ b | a.symmetric_difference(b) | In one but not both |
a = {1, 2, 3, 4, 5}
b = {4, 5, 6, 7, 8}
print(a | b) # {1, 2, 3, 4, 5, 6, 7, 8} — union
print(a & b) # {4, 5} — intersection
print(a - b) # {1, 2, 3} — difference
print(b - a) # {6, 7, 8} — difference (other way)
print(a ^ b) # {1, 2, 3, 6, 7, 8} — symmetric difference
# Subset / superset checks
small = {1, 2}
big = {1, 2, 3, 4, 5}
print(small <= big) # True (small is a subset of big)
print(big >= small) # True (big is a superset of small)
print(small < big) # True (proper subset — not equal)
# Disjoint check — no common elements
print({1, 2}.isdisjoint({3, 4})) # True
set_operations.py
Practical Set Patterns
# Remove duplicates from a list (fast but loses order)
items = [1, 3, 2, 3, 1, 4, 2]
unique = list(set(items))
print(unique) # order not guaranteed
# Preserve order while removing duplicates (Python 3.7+)
unique_ordered = list(dict.fromkeys(items))
print(unique_ordered) # [1, 3, 2, 4]
# Fast membership testing (O(1) vs O(n) for lists)
valid_codes = {"US", "UK", "CA", "AU", "DE", "FR"}
user_code = "CA"
if user_code in valid_codes:
print("Valid country")
# Find common friends
alice_friends = {"Bob", "Charlie", "Dave", "Eve"}
bob_friends = {"Alice", "Charlie", "Eve", "Frank"}
mutual = alice_friends & bob_friends
print(mutual) # {'Charlie', 'Eve'}
# Find skills a candidate is missing
required = {"python", "sql", "git", "docker"}
candidate = {"python", "git", "javascript"}
missing = required - candidate
print(f"Missing: {missing}") # {'sql', 'docker'}
set_patterns.py
x in set is O(1) on average — constant time
regardless of set size. For lists it's O(n). If you're doing many
membership checks, convert to a set first.
Frozenset
A frozenset is an immutable set. It supports all read operations
and set math, but cannot be modified. Because it's immutable, it can be used as a
dictionary key or an element of another set.
fs = frozenset([1, 2, 3, 4])
# All read operations work
print(3 in fs) # True
print(fs | {5, 6}) # frozenset({1, 2, 3, 4, 5, 6})
# Mutation is not allowed
# fs.add(5) ← AttributeError
# Can be used as a dict key or set element
cache = {frozenset({1, 2}): "result_a"}
nested_sets = {frozenset({1, 2}), frozenset({3, 4})}
frozenset.py
Primary sources: Python Docs — Tuples · Python Docs — Sets
Ask your AI tutor! Not sure when to use a tuple vs a list vs a set?
Confused about why {} is a dict and not an empty set? These are common
points of confusion — ask and get clarity.
💻 Exercises
Write a function stats(numbers) that takes a list of numbers and
returns a tuple of (minimum, maximum, average).
Call it and unpack the result into three variables.
Show solution
def stats(numbers):
return min(numbers), max(numbers), sum(numbers) / len(numbers)
data = [23, 45, 12, 67, 34, 89, 2]
lo, hi, avg = stats(data)
print(f"Min: {lo}, Max: {hi}, Avg: {avg:.2f}")
# Min: 2, Max: 89, Avg: 38.86
Given two lists: [1, 2, 3, 4, 5, 6] and [4, 5, 6, 7, 8, 9],
use set operations to find: elements in both, elements only in the first,
and all unique elements combined. Print each result.
Show solution
list_a = [1, 2, 3, 4, 5, 6]
list_b = [4, 5, 6, 7, 8, 9]
set_a = set(list_a)
set_b = set(list_b)
common = set_a & set_b
only_a = set_a - set_b
all_unique = set_a | set_b
print(f"In both: {sorted(common)}") # [4, 5, 6]
print(f"Only in A: {sorted(only_a)}") # [1, 2, 3]
print(f"All unique: {sorted(all_unique)}") # [1, 2, 3, 4, 5, 6, 7, 8, 9]
Given the string "the cat sat on the mat the cat", find:
how many total words there are, how many unique words,
and list the unique words sorted alphabetically.
Show solution
text = "the cat sat on the mat the cat"
words = text.split()
unique_words = set(words)
print(f"Total words: {len(words)}") # 8
print(f"Unique words: {len(unique_words)}") # 5
print(f"Sorted: {sorted(unique_words)}")
# ['cat', 'mat', 'on', 'sat', 'the']