Follow AiTechWorlds on LinkedIn for professional AI content!Follow Now →
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 this course's notes on Telegram!

Free cheat sheets, summaries & practice exercises

Get Notes Free →
!