14 minLesson 9 of 34
Data Structures
Tuples, Sets & Frozen Sets
Tuples, Sets & Frozen Sets: Python's Other Data Structures
Python has four built-in collection types: lists, dictionaries, tuples, and sets. Tuples and sets are underused by beginners but essential in real Python code. Knowing when to reach for each one is a mark of experience.
Tuples: Immutable Sequences
A tuple looks like a list but can never be modified after creation. That immutability is the point — it signals "this data won't change."
# Creating tuples
point = (3, 4)
rgb = (255, 128, 0)
single = (42,) # Note the comma — (42) is just an int
empty = ()
# Tuple packing — the parentheses are optional
coordinates = 10.5, 20.3
print(type(coordinates)) # <class 'tuple'>
# Unpacking — Python's most elegant feature
x, y = point
print(x, y) # 3 4
# Extended unpacking
first, *rest = (1, 2, 3, 4, 5)
print(first) # 1
print(rest) # [2, 3, 4, 5]
a, *middle, z = range(10)
print(a, middle, z) # 0 [1, 2, 3, 4, 5, 6, 7, 8] 9
Tuple Use Cases
# 1. Function returning multiple values (returns a tuple)
def min_max(numbers):
return min(numbers), max(numbers) # Returns tuple
low, high = min_max([3, 1, 4, 1, 5, 9, 2, 6])
print(low, high) # 1 9
# 2. Dictionary keys (lists can't be dict keys; tuples can)
locations = {}
locations[(40.7128, -74.0060)] = "New York"
locations[(51.5074, -0.1278)] = "London"
# 3. Named tuples — like lightweight objects
from collections import namedtuple
Point = namedtuple('Point', ['x', 'y'])
p = Point(x=3, y=4)
print(p.x, p.y) # 3 4
print(p) # Point(x=3, y=4)
# Useful for returning structured data without defining a full class
Color = namedtuple('Color', ['red', 'green', 'blue'])
red = Color(255, 0, 0)
print(f"RGB: {red.red}, {red.green}, {red.blue}")
# 4. Swap values elegantly
a, b = 10, 20
a, b = b, a # The Pythonic swap — no temp variable needed
print(a, b) # 20 10
# 5. Tuple vs list performance — tuples are slightly faster
import timeit
list_time = timeit.timeit('[1, 2, 3, 4, 5]', number=1000000)
tuple_time = timeit.timeit('(1, 2, 3, 4, 5)', number=1000000)
print(f"List: {list_time:.3f}s, Tuple: {tuple_time:.3f}s")
# Tuples are ~10-30% faster for creation and iteration
Sets: Unordered Collections of Unique Items
Sets store unique elements with no duplicates and no ordering. The power is in their mathematical operations and O(1) membership testing.
# Creating sets
fruits = {"apple", "banana", "cherry"}
numbers = {1, 2, 3, 4, 5}
empty_set = set() # Note: {} creates an empty dict, not a set!
# From a list — removes duplicates
tags = set(["python", "data", "python", "ml", "data"])
print(tags) # {'python', 'data', 'ml'} — no duplicates
# Checking membership — O(1) vs O(n) for lists
large_set = set(range(1000000))
large_list = list(range(1000000))
import timeit
set_time = timeit.timeit('999999 in large_set', globals=locals(), number=1000)
list_time = timeit.timeit('999999 in large_list', globals=locals(), number=1000)
print(f"Set lookup: {set_time:.4f}s") # Very fast
print(f"List lookup: {list_time:.4f}s") # Much slower
Set Operations: Mathematical Power
python_devs = {"Alice", "Bob", "Carol", "Dave"}
data_scientists = {"Bob", "Carol", "Eve", "Frank"}
# Union: all members from either group
all_devs = python_devs | data_scientists
# OR: python_devs.union(data_scientists)
print(all_devs) # {'Alice', 'Bob', 'Carol', 'Dave', 'Eve', 'Frank'}
# Intersection: members in BOTH groups
both = python_devs & data_scientists
# OR: python_devs.intersection(data_scientists)
print(both) # {'Bob', 'Carol'}
# Difference: in first but NOT in second
only_python = python_devs - data_scientists
# OR: python_devs.difference(data_scientists)
print(only_python) # {'Alice', 'Dave'}
# Symmetric difference: in one but NOT both
exclusive = python_devs ^ data_scientists
# OR: python_devs.symmetric_difference(data_scientists)
print(exclusive) # {'Alice', 'Dave', 'Eve', 'Frank'}
# Subset / Superset
small = {"Bob", "Carol"}
print(small.issubset(python_devs)) # True
print(python_devs.issuperset(small)) # True
print(small.isdisjoint({"Alice"})) # True (no common elements)
Common Set Patterns
# 1. Remove duplicates while preserving order (Python 3.7+)
def deduplicate(items):
seen = set()
return [x for x in items if not (x in seen or seen.add(x))]
dupes = [3, 1, 4, 1, 5, 9, 2, 6, 5, 3]
print(deduplicate(dupes)) # [3, 1, 4, 5, 9, 2, 6]
# Note: list(set(items)) also removes dupes but loses order
# 2. Find common elements between lists
list_a = [1, 2, 3, 4, 5]
list_b = [3, 4, 5, 6, 7]
common = set(list_a) & set(list_b)
print(common) # {3, 4, 5}
# 3. Track visited items efficiently
def find_first_duplicate(numbers):
seen = set()
for num in numbers:
if num in seen:
return num
seen.add(num)
return None
print(find_first_duplicate([1, 2, 3, 4, 2])) # 2
# 4. Data validation — allowed values
VALID_STATUSES = {"pending", "active", "completed", "cancelled"}
def validate_status(status):
if status not in VALID_STATUSES:
raise ValueError(f"Invalid status: {status}. Must be one of {VALID_STATUSES}")
return status
# Set operations on real data
email_list_a = {"alice@ex.com", "bob@ex.com", "carol@ex.com"}
email_list_b = {"bob@ex.com", "dave@ex.com", "carol@ex.com"}
unsubscribed = {"bob@ex.com"}
active_a = email_list_a - unsubscribed
new_subscribers = email_list_b - email_list_a
print(f"New subscribers: {new_subscribers}")
Mutable vs Immutable: Set Modifications
fruits = {"apple", "banana"}
# Add and remove
fruits.add("cherry") # Add one
fruits.update(["date", "fig"]) # Add multiple
fruits.discard("grape") # Remove if exists (no error if missing)
fruits.remove("banana") # Remove; raises KeyError if missing
popped = fruits.pop() # Remove and return arbitrary element
fruits.clear() # Remove all elements
Frozen Sets: Immutable Sets
# frozenset: immutable version of set
# Can be used as dict keys or stored in other sets
permissions = frozenset(["read", "write"])
admin_permissions = frozenset(["read", "write", "delete", "admin"])
# Can be dict key (regular sets cannot)
role_permissions = {
frozenset(["read"]): "viewer",
frozenset(["read", "write"]): "editor",
frozenset(["read", "write", "delete", "admin"]): "admin"
}
user_perms = frozenset(["read", "write"])
role = role_permissions.get(user_perms, "unknown")
print(role) # editor
Choosing Between List, Tuple, Set, and Dict
List → Ordered, mutable, allows duplicates
"I need a sequence I'll modify" → shopping_cart = []
Tuple → Ordered, immutable, allows duplicates
"Fixed data, multiple return values, dict keys" → coordinates = (x, y)
Set → Unordered, mutable, NO duplicates
"Membership testing, deduplication, set math" → seen_ids = set()
Dict → Key-value pairs, mutable, unique keys
"Lookup by key" → user = {"name": "Alice", "age": 30}
Next lesson: List & Dict Comprehensions — Python's most elegant syntax for creating collections.
📱
Get Notes Free →Get this course's notes on Telegram!
Free cheat sheets, summaries & practice exercises