Blog Post #81: Need an Immutable Set? Meet the frozenset

We’ve learned that sets are mutable collections, meaning we can add and remove items after they’re created. This behavior is similar to lists. But just as lists have an immutable counterpart in tuples (which we learned about in Post #58), sets also have an immutable version for situations where you need a guarantee that the collection will not change.

In this post, we’ll be introduced to the frozenset. We’ll learn what it is, how it differs from a regular set, and explore the key situations where this special data type is required.

What is a frozenset?

A frozenset is an immutable version of a set. It has the same core properties of being an unordered collection of unique elements, but with one major difference: once a frozenset is created, its contents cannot be altered in any way.

You create a frozenset using its constructor, passing in any iterable (like a list, tuple, or another set).

# Create a frozenset from a list with duplicates
frozen = frozenset([1, 2, 2, 3])

print(frozen)
print(type(frozen))

The output will be:

frozenset({1, 2, 3})
<class 'frozenset'>

Because it is immutable, a frozenset does not have methods like .add(), .remove(), or .pop(). Attempting to call them will result in an AttributeError. However, all the non-modifying operations we learned about, like union (|) and intersection (&), work perfectly with frozensets.

The Main Use Case: Hashability

So why would we want an unchangeable set? The primary reason is that frozenset objects are hashable, while regular set objects are not. As we learned in our discussion on dictionary performance (Post #73), an object must be hashable (and therefore immutable) to be used as a dictionary key.

This opens up two important possibilities.

Using a frozenset as a Dictionary Key

You cannot use a mutable set as a dictionary key, as it can change. But you can use a frozenset. This is useful if you want to map a collection of unique items to a value.

# A regular set cannot be a dictionary key
my_set = {1, 2}
# my_dict = {my_set: "value"} # This would cause a TypeError!

# A frozenset CAN be a dictionary key
my_frozenset = frozenset([1, 2])
my_dict = {my_frozenset: "value"}

print(my_dict)

The output is a valid dictionary: {frozenset({1, 2}): 'value'}.

Using a frozenset Inside a Set

For the same reason, the items contained within a set must also be immutable. This means you cannot have a set of sets. However, you can have a set of frozensets.

set1 = {1, 2}
set2 = {3, 4}

# This will cause a TypeError: unhashable type: 'set'
# set_of_sets = {set1, set2}

# This is the correct way to do it
frozen1 = frozenset(set1)
frozen2 = frozenset(set2)
set_of_frozensets = {frozen1, frozen2}

print(set_of_frozensets)

The output will be: {frozenset({1, 2}), frozenset({3, 4})}.

What’s Next?

While you may not use it as often as regular sets, the frozenset is an important tool to have in your knowledge base. It provides an immutable version of a set, making it possible to use set-like data as dictionary keys or as elements within other sets.

This post concludes our introduction to Python’s four primary built-in data structures. We’ve covered lists, tuples, dictionaries, and sets. To solidify everything we’ve learned in this section, it’s time for one final project. In Post #82, we will use the power of sets to analyze the commonalities and differences between two lists of data.

Author

Debjeet Bhowmik

Experienced Cloud & DevOps Engineer with hands-on experience in AWS, GCP, Terraform, Ansible, ELK, Docker, Git, GitLab, Python, PowerShell, Shell, and theoretical knowledge on Azure, Kubernetes & Jenkins. In my free time, I write blogs on ckdbtech.com

Leave a Comment