Frozen Set
A frozen set is an immutable version of a set. Frozen sets cannot be changed after creation which means that elements cannot be added, removed, or modified. Since they are immutable, frozen sets are hashable and can be used as dictionary keys or as elements in other sets.
Key Properties of Frozen Sets
- Immutable: Cannot be modified after creation
- Hashable: Can be used as dictionary keys and set elements
- Unordered: Elements have no defined order (like regular sets)
- Unique elements: No duplicates allowed
- Unindexed: Elements cannot be accessed by index
Creating a Frozen Set
Frozen sets are created using the frozenset() constructor, which takes an iterable:
# From a listfs = frozenset([1, 2, 3])print(fs) # frozenset({1, 2, 3})
# From a stringchars = frozenset("hello") # frozenset({'h', 'e', 'l', 'o'})
# From a rangenumbers = frozenset(range(5)) # frozenset({0, 1, 2, 3, 4})
# Empty frozen setempty = frozenset()print(empty) # frozenset()Note
Like regular sets, frozen sets automatically remove duplicates and are unordered. The order of elements when printing may vary.
Immutability and Hashability
The key advantage of frozen sets is their immutability, which makes them hashable:
fs = frozenset([1, 2, 3])
# Frozen sets are hashableprint(hash(fs)) # Some hash value
# Can be used as dictionary keysd = {fs: "value"}print(d[fs]) # value
# Can be used as set elementsset_of_frozen_sets = {frozenset([1, 2]), frozenset([3, 4])}print(set_of_frozen_sets) # {frozenset({1, 2}), frozenset({3, 4})}Tip
Regular sets cannot be used as dictionary keys or set elements because they are mutable and therefore unhashable. Frozen sets solve this limitation.
Checking Membership
Elements can be checked for existence in a frozen set using the in and not in operators:
fs = frozenset([1, 2, 3, 4, 5])
print(3 in fs) # Trueprint(10 in fs) # Falseprint(2 not in fs) # FalseLooping Over a Frozen Set
Frozen sets can be iterated over using a for loop:
fs = frozenset([1, 2, 3, 4, 5])
for element in fs: print(element)Frozen Set Operations
Frozen sets support all set operations that return new sets (since they cannot be modified in place):
union(iterable1, iterable2, ...): Returns a new frozen set containing all elements from the frozen set and all given iterables .intersection(iterable1, iterable2, ...): Returns a new frozen set containing only elements that are common to the frozen set and all given iterables .difference(iterable1, iterable2, ...): Returns a new frozen set containing elements that are in the frozen set but not in any of the given iterables .symmetric_difference(iterable): Returns a new frozen set containing elements that are in either the frozen set or the iterable, but not in both .issubset(iterable): ReturnsTrueif all elements of the frozen set are in the given iterable.issuperset(iterable): ReturnsTrueif all elements of the given iterable are in the frozen set.isdisjoint(iterable): ReturnsTrueif the frozen set and the given iterable have no elements in common (their intersection is empty).
fs1 = frozenset([1, 2, 3])fs2 = frozenset([3, 4, 5])result = fs1.union(fs2)print(result) # frozenset({1, 2, 3, 4, 5})
# Can also use the | operatorresult = fs1 | fs2 # frozenset({1, 2, 3, 4, 5})fs1 = frozenset([1, 2, 3, 4])fs2 = frozenset([3, 4, 5, 6])result = fs1.intersection(fs2)print(result) # frozenset({3, 4})
# Can also use the & operatorresult = fs1 & fs2 # frozenset({3, 4})fs1 = frozenset([1, 2, 3, 4, 5])fs2 = frozenset([3, 4])result = fs1.difference(fs2)print(result) # frozenset({1, 2, 5})
# Can also use the - operatorresult = fs1 - fs2 # frozenset({1, 2, 5})fs1 = frozenset([1, 2, 3, 4])fs2 = frozenset([3, 4, 5, 6])result = fs1.symmetric_difference(fs2)print(result) # frozenset({1, 2, 5, 6})
# Can also use the ^ operatorresult = fs1 ^ fs2 # frozenset({1, 2, 5, 6})fs1 = frozenset([1, 2, 3])fs2 = frozenset([1, 2, 3, 4, 5])print(fs1.issubset(fs2)) # True
# Can also use the <= operatorprint(fs1 <= fs2) # Truefs1 = frozenset([1, 2, 3, 4, 5])fs2 = frozenset([1, 2, 3])print(fs1.issuperset(fs2)) # True
# Can also use the >= operatorprint(fs1 >= fs2) # Truefs1 = frozenset([1, 2, 3])fs2 = frozenset([4, 5, 6])print(fs1.isdisjoint(fs2)) # True
fs3 = frozenset([3, 4, 5])print(fs1.isdisjoint(fs3)) # False (they share element 3)Why Use Frozen Sets?
1. Dictionary Keys
Frozen sets can be used as dictionary keys, which is useful for representing relationships or mappings:
# Representing edges in a graphedges = { frozenset([1, 2]): "edge_1_2", frozenset([2, 4]): "edge_2_4", frozenset([1, 3]): "edge_1_3"}
print(edges[frozenset([1, 2])]) # edge_1_22. Sets of Sets
Frozen sets can be elements in other sets, allowing for sets of sets:
# Graph representation: set of edges (each edge is a frozen set)graph = { frozenset([1, 2]), frozenset([2, 4]), frozenset([1, 3])}
print(graph) # {frozenset({1, 2}), frozenset({2, 4}), frozenset({1, 3})}Exercises
Exercise 1: Create a frozen set from a list
Write a program that reads the size of a list, then reads the elements, creates a frozen set from the list, and prints it.
Create Frozen Set
n = int(input())elements = []for i in range(n): elements.append(input())fs = frozenset(elements)print(fs)Exercise 2: Use frozen set as dictionary key
Write a program that creates a dictionary where keys are frozen sets representing pairs of numbers, and values are strings describing the pair. Create entries for pairs (1,2), (2,3), and (1,3), then print the dictionary.
Frozen Set as Dictionary Key
d = { frozenset([1, 2]): "pair_1_2", frozenset([2, 3]): "pair_2_3", frozenset([1, 3]): "pair_1_3"}print(d)Exercise 3: Set of frozen sets (graph edges)
Write a program that creates a set of frozen sets to represent graph edges. Create edges (1,2), (2,3), (3,4), and (1,4), then print the set.
Set of Frozen Sets
graph = { frozenset([1, 2]), frozenset([2, 3]), frozenset([3, 4]), frozenset([1, 4])}print(graph)Exercise 4: Dictionary with frozen set keys
Write a program that reads the number of entries n, then for each entry reads two numbers representing a pair, creates a frozen set from the pair, and uses it as a key in a dictionary with value "edge_{first}_{second}". Print the dictionary.
Dictionary with Frozen Set Keys
n = int(input())d = {}for i in range(n): first = int(input()) second = int(input()) key = frozenset([first, second]) d[key] = f"edge_{first}_{second}"print(d)Exercise 5: Count unique pairs using frozen sets
Write a program that reads the number of pairs n, then for each pair reads two numbers, creates a frozen set for each pair, adds them to a set, and prints the count of unique pairs (where order doesn’t matter, so (1,2) and (2,1) are the same).
Count Unique Pairs
n = int(input())unique_pairs = set()for i in range(n): first = int(input()) second = int(input()) pair = frozenset([first, second]) unique_pairs.add(pair)print(len(unique_pairs))Exercise 6: Graph representation with frozen sets
Write a program that reads the number of edges n, then for each edge reads two vertices, creates a graph as a set of frozen sets (each edge is a frozen set), and prints the graph. Then check if edge (1,2) exists in the graph and print "Yes" or "No".
Graph Representation
n = int(input())graph = set()for i in range(n): v1 = int(input()) v2 = int(input()) edge = frozenset([v1, v2]) graph.add(edge)
print(graph)
edge_to_check = frozenset([1, 2])if edge_to_check in graph: print("Yes")else: print("No")