Master Guide to Sets and Frozensets in Python: Operations and Comparisons

Hello Everyone! Today we are exploring Sets and Frozensets in Python.

Imagine a bag where you throw various items in. If you try to add an item that’s already in the bag, the bag simply ignores it. Furthermore, the items don’t stay in any particular order. That is exactly how a Python set works. It is the perfect data structure for removing duplicates and performing mathematical group operations.

1. What is a Set?

  • Unordered: Sets do not have index positions. Because there is no fixed sequence order, sets do not support indexing or slicing.
  • Mutable: You can dynamically insert or discard values after creation.
  • Unique Elements: Sets filter out duplicates automatically. Every item must be unique.
  • Heterogeneous (with a catch): A set can hold mixed data types, but all elements must be hashable (immutable types like numbers, strings, or tuples). Mutable objects like lists or other sets cannot be placed inside a set.

Crucial Internal Type Deduplication

In Python, certain distinct data types evaluate to the exact same value internally:

  • 1 and True are evaluated as the same boolean/numeric value.
  • 0 and False are evaluated as the same boolean/numeric value.
# Duplicates, True (same as 1), and False (same as 0) are collapsed automatically
a = {1, 2, 3, 1, 2, "Vipin", "vipin", True, 0, False}
print(a)  # Output: {0, 1, 2, 3, 'vipin', 'Vipin'} (Order may vary!)

Initializing Empty Sets vs. Literals

To create an empty set, you must use set(). Using empty curly braces {} creates an empty dictionary, not a set!

empty_set = set()      # Valid empty set
not_a_set = {}         # This is a dictionary literal!

# Building sets from string sequences
my_set = set("Rajasthan")
print(my_set)          # Output: {'j', 'a', 'R', 's', 't', 'h', 'n'} (Unique characters only)


2. Adding & Modifying Elements

Python provides two distinct methods to add elements, and they behave quite differently:

  • .add(item): Inserts a single, un-iterable immutable item.
  • .update(iterable): Unpacks an entire collection (like a list, tuple, or another set) and merges its individual elements into the set.
my_set = {1, 2, 3}

my_set.add((4, 5))    # Works! A tuple is immutable/hashable.
# my_set.add([6, 7])  # Crashes with TypeError! Lists are unhashable.

my_set.update([4, 5, 6]) 
print(my_set)          # Output: {1, 2, 3, 4, 5, 6, (4, 5)}


3. Removing Elements Safely

When removing items, choose your method carefully based on how you want to handle missing values:

  • .remove(value): Deletes an item. If the item does not exist, **it crashes the program with a KeyError**.
  • .discard(value): Deletes an item. If the item does not exist, it silently ignores it without throwing an error.
  • .pop(): Removes and returns an arbitrary item from the set. Unlike lists, you cannot pass an index argument because sets are unordered.
my_set = {10, 20, 30}

my_set.discard(90)   # Safe! Nothing happens.
# my_set.remove(90)  # Crashes with KeyError!

popped = my_set.pop() # Removes a random element


4. Mathematical Set Operations

Sets are highly efficient for performing core mathematical Venn diagram operations using either method calls or native operator symbols.

A. Union (|)

Combines all unique elements from both sets.

set_a = {1, 2, 3, 4}
set_b = {3, 4, 5, 6}

print(set_a | set_b)                  # Output: {1, 2, 3, 4, 5, 6}
print(set_a.union(set_b))             # Identical output

B. Intersection (&)

Extracts only the elements common to both sets.

print(set_a & set_b)                  # Output: {3, 4}
print(set_a.intersection(set_b))      # Identical output

C. Difference (-)

Returns elements that exist in the first set but not in the second set.

print(set_a - set_b)                  # Output: {1, 2} (In A, not in B)
print(set_b - set_a)                  # Output: {5, 6} (In B, not in A)

D. Symmetric Difference (^)

Returns all elements across both sets except for the items they share in common (the opposite of intersection).

print(set_a ^ set_b)                  # Output: {1, 2, 5, 6}
print(set_a.symmetric_difference(set_b)) # Identical output


5. Relationships and Boundary Checks

You can check how sets relate to each other using boolean comparison flags:

set1 = {1, 2, 3}
set2 = {1, 2, 3, 4, 5}
set3 = {8, 9}

# Subset Check (Is everything in set1 inside set2?)
print(set1.issubset(set2))    # Output: True
print(set1 <= set2)           # Operators shorthand

# Superset Check (Does set2 contain everything in set1?)
print(set2.issuperset(set1))  # Output: True

# Disjoint Check (Do they share absolutely ZERO items in common?)
print(set1.isdisjoint(set3))  # Output: True (No overlapping elements)


6. Immutable Sets: Frozensets

A frozenset is a set that is completely immutable. It supports all mathematical group operations like union or intersection, but any method that attempts to alter the contents (.add(), .update(), .pop(), .remove()) is completely stripped away.

fs = frozenset([1, 2, 3, 3])
print(fs)  # Output: frozenset({1, 2, 3})

# fs.add(4) # Crashes with AttributeError: 'frozenset' object has no attribute 'add'

Why use a frozenset? Since a frozenset is immutable, it is hashable. This means you can actually nest a frozenset inside another standard set, whereas nesting a regular mutable set would fail!


7. Master Comparison: Python Built-in Sequences & Collections

Here is a complete summary reference table comparing all the major collection types we have covered so far:

Feature List Tuple Set Frozenset String
Syntax Layout [ ] ( ) { } frozenset() ' ' or " "
Ordered ✅ Yes ✅ Yes ❌ No ❌ No ✅ Yes
Mutable (Modifiable) ✅ Yes ❌ No ✅ Yes ❌ No ❌ No
Duplicates Allowed ✅ Yes ✅ Yes ❌ No ❌ No ✅ Yes
Indexing/Slicing ✅ Yes ✅ Yes ❌ No ❌ No ✅ Yes
Elements Allowed Any Type Any Type Only Hashable Types Only Hashable Types Single Characters
Primary Use Case Dynamic sequence tracking Fixed database records Unique values & Venn math Immutable unique keys Text processing