Sets are mathematical objects. They support operations like union, intersection, difference, and symmetric difference. You can check if one set is a subset of another. You can create frozensets (immutable sets) for use as dictionary keys. You can perform complex set algebra.
This lesson explores advanced set operations. You will learn to test subsets and supersets, find disjoint sets, use set comprehensions, work with frozensets, and apply set operations to real-world problems like finding common interests, analyzing user permissions, and deduplication pipelines.
🕯️ Magic Note
Sets are implemented using hash tables, making membership tests O(1) on average. This makes sets incredibly fast for checks like if item in set:, even for very large sets (millions of items). The same operation on a list is O(n).
Python
A = {1, 2, 3, 4, 5}
B = {1, 2, 3}
C = {1, 2, 3, 4, 5, 6}
D = {6, 7, 8}
# issubset() – all elements of first are in second
print(B.issubset(A)) # True (B ⊆ A)
print(B.issubset(C)) # True
print(A.issubset(B)) # False
print(D.issubset(A)) # False
# issuperset() – all elements of second are in first
print(A.issuperset(B)) # True (A ⊇ B)
print(C.issuperset(A)) # True
print(B.issuperset(A)) # False
# Subset with operators (<= and <)
print(B <= A) # True (subset, could be equal)
print(B < A) # True (proper subset, cannot be equal)
print(A <= A) # True (subset includes equality)
print(A < A) # False (proper subset does not include equality)
# Superset with operators (>= and >)
print(A >= B) # True
print(A > B) # True
Python
A = {1, 2, 3, 4, 5}
B = {6, 7, 8}
C = {4, 5, 6, 7}
# isdisjoint() returns True if no common elements
print(A.isdisjoint(B)) # True (no overlap)
print(A.isdisjoint(C)) # False (share 4 and 5)
print(B.isdisjoint(C)) # False (share 6 and 7)
# Equivalent to intersection being empty
print(len(A & B) == 0) # True
print(A & B == set()) # True
🕯️ Magic Note
The isdisjoint() method is more efficient than checking intersection because it stops as soon as any common element is found, without creating a new set.
Python
A = {1, 2, 3}
B = {3, 4, 5}
# In-place union (adds elements from B to A)
A.update(B)
print(A) # {1, 2, 3, 4, 5}
A = {1, 2, 3, 4, 5}
# In-place intersection (keeps only elements in both)
A.intersection_update(B)
print(A) # {3, 4, 5}
A = {1, 2, 3, 4, 5}
# In-place difference (removes elements in B)
A.difference_update(B)
print(A) # {1, 2}
A = {1, 2, 3, 4, 5}
# In-place symmetric difference (elements in either but not both)
A.symmetric_difference_update(B)
print(A) # {1, 2} (from A) union {4,5}? Wait: symmetric = {1,2} ∪ {4,5} = {1,2,4,5}
# Let me recalc: A={1,2,3,4,5}, B={3,4,5}, symmetric = {1,2} (only in A) plus empty from B = {1,2}
Python
# Creating frozensets
fs1 = frozenset([1, 2, 3, 3, 2]) # frozenset({1, 2, 3})
fs2 = frozenset({3, 4, 5})
print(fs1) # frozenset({1, 2, 3})
print(type(fs1)) # <class ‘frozenset’>
# Frozensets support set operations
print(fs1 | fs2) # frozenset({1, 2, 3, 4, 5})
print(fs1 & fs2) # frozenset({3})
print(fs1 – fs2) # frozenset({1, 2})
# Cannot modify frozensets
# fs1.add(4) # AttributeError: ‘frozenset’ object has no attribute ‘add’
# Frozensets as dictionary keys (regular sets cannot be keys)
dict_with_frozenset = {
frozenset([1, 2]): “pair”,
frozenset([1, 2, 3]): “triple”,
frozenset([1, 2, 3, 4]): “quad”
}
print(dict_with_frozenset[frozenset([1, 2])]) # ‘pair’
# Convert set to frozenset
s = {1, 2, 3}
fs = frozenset(s)
🕯️ Magic Note
Frozensets are hashable because they are immutable. This makes them useful as dictionary keys or elements of other sets. Regular sets are unhashable and cannot be used in these contexts.
Python
# Basic set comprehension
squares = {x**2 for x in range(10)}
print(squares) # {0, 1, 4, 9, 16, 25, 36, 49, 64, 81}
# Set comprehension with filter
even_squares = {x**2 for x in range(20) if x % 2 == 0}
print(even_squares) # {0, 4, 16, 36, 64, 100, 144, 196, 256, 324}
# Set comprehension from another iterable
text = “hello world”
unique_chars = {char for char in text if char != ” “}
print(unique_chars) # {‘h’, ‘e’, ‘l’, ‘o’, ‘w’, ‘r’, ‘d’}
# Nested set comprehension (flattening)
matrix = [[1, 2, 3], [4, 2, 6], [7, 8, 9]]
unique_values = {val for row in matrix for val in row}
print(unique_values) # {1, 2, 3, 4, 6, 7, 8, 9}
# Set comprehension with conditional expression
numbers = [1, 2, 3, 4, 5]
labels = {“even” if x % 2 == 0 else “odd” for x in numbers}
print(labels) # {‘odd’, ‘even’}
Python
# User interests as sets
ali = {“Python”, “Coffee”, “Magic”, “Books”, “Music”}
sara = {“Python”, “Coffee”, “Travel”, “Photography”, “Music”}
reza = {“Java”, “Coffee”, “Sports”, “Movies”, “Games”}
# Common interests between Ali and Sara
common_ali_sara = ali & sara
print(f”Ali and Sara share: {common_ali_sara}”)
# Interests unique to Ali (not in any other set)
unique_ali = ali – (sara | reza)
print(f”Unique to Ali: {unique_ali}”)
# Interests that only one person has
all_interests = ali | sara | reza
def exclusive_interests(*sets):
“””Return interests that appear in exactly one set.”””
result = set()
all_items = set().union(*sets)
for item in all_items:
count = sum(1 for s in sets if item in s)
if count == 1:
result.add(item)
return result
exclusive = exclusive_interests(ali, sara, reza)
print(f”Exclusive interests: {exclusive}”)
# Interests shared by at least two people
pairwise_common = (ali & sara) | (ali & reza) | (sara & reza)
print(f”Interests shared by at least two: {pairwise_common}”)
# Suggest new interests based on what similar users like
def suggest_interests(user, similar_user, user_interests, all_interests):
“””Suggest interests from similar user that the current user does not have.”””
return similar_user – user
suggestions = suggest_interests(ali, sara, ali, all_interests)
print(f”Suggestions for Ali based on Sara: {suggestions}”)
Python
import csv
from pathlib import Path
def deduplicate_csv(input_file, output_file, key_field=”email”):
“””Remove duplicate rows from a CSV file based on a key field.”””
seen = set()
unique_rows = []
with open(input_file, “r”, encoding=”utf-8″) as f:
reader = csv.DictReader(f)
fieldnames = reader.fieldnames
for row in reader:
key = row.get(key_field)
if key and key not in seen:
seen.add(key)
unique_rows.append(row)
with open(output_file, “w”, encoding=”utf-8″, newline=””) as f:
writer = csv.DictWriter(f, fieldnames=fieldnames)
writer.writeheader()
writer.writerows(unique_rows)
print(f”Deduplicated {len(seen)} unique records out of {len(seen) + (len(unique_rows) – len(seen))} total”)
# Example: Find duplicate files by content hash
import hashlib
def find_duplicate_files(directory):
“””Find duplicate files in a directory using MD5 hashes.”””
seen_hashes = set()
duplicates = []
for file_path in Path(directory).rglob(“*”):
if file_path.is_file():
with open(file_path, “rb”) as f:
file_hash = hashlib.md5(f.read()).hexdigest()
if file_hash in seen_hashes:
duplicates.append(file_path)
else:
seen_hashes.add(file_hash)
return duplicates
Python
# Sets use more memory than lists (overhead for hash table)
import sys
lst = list(range(1000000))
st = set(range(1000000))
print(f”List memory: {sys.getsizeof(lst) / 1024 / 1024:.2f} MB”)
print(f”Set memory: {sys.getsizeof(st) / 1024 / 1024:.2f} MB”)
# If you only need to check membership, use set
# If you need order and allow duplicates, use list
# If you need order but no duplicates, use dict.fromkeys()
🕯️ Magic Note
Sets are optimized for speed, not memory. The hash table overhead can be 2-3x the size of the data. For very large sets, consider using bloom filters (third-party library) for probabilistic membership tests with much lower memory usage.
- Expecting sets to preserve order (they do not in Python versions before 3.7; even after 3.7, order is insertion order but not guaranteed for all operations)
- Using mutable elements in sets (lists, dicts, other sets)
- Forgetting that update() modifies in place and returns None
- Using regular sets as dictionary keys (use frozenset)
- Assuming isdisjoint() creates an intersection (it does not, it stops early)
- Write a function that checks if one set is a subset of another without using built-in methods.
- What is the difference between issubset() and the <= operator?
- Create a frozenset and use it as a key in a dictionary.
- Write a set comprehension that creates a set of vowels from a given string.
- How do you find the elements that are in set A but not in set B or set C?
- When would you use intersection_update() instead of intersection()?
⚡ Whisper
Sets are the mathematicians’ gift to programmers. They speak the language of Venn diagrams. Union, intersection, difference, subset. With sets, you ask: “What do these groups share?” The answer comes instantly. “What is unique to this group?” The difference appears. “Is this group entirely contained in that one?” The subset test decides. Sets are fast. They are exact. They eliminate duplicates without thought. They find overlaps without loops. For permissions, tags, categories, interests, deduplication, set operations are unmatched. Learn the operators. Master the methods. Then apply them to your data. The relationships between sets will reveal insights you never saw. The sets are waiting. Their logic is clear. Use it.