Of course! Let's dive deep into Python's frozenset.

What is a frozenset?
A frozenset is a Python built-in data type that is an immutable version of the standard set.
Think of it like this:
- A
setis like a box of building blocks. You can add new blocks, remove blocks, or swap blocks around. It's mutable (changeable). - A
frozensetis like a perfectly cast statue made from those blocks. Once it's made, you can't change it in any way. It's immutable (unchangeable).
The Key Difference: set vs. frozenset
| Feature | set |
frozenset |
|---|---|---|
| Mutability | Mutable (can be changed after creation) | Immutable (cannot be changed after creation) |
| Methods to Modify | .add(), .remove(), .discard(), .pop(), .update(), etc. |
None. No methods to add or remove elements. |
| Hashable | No. Cannot be used as a key in a dictionary or as an element in another set. | Yes. Can be used as a key in a dictionary or as an element in another set. |
| Syntax | my_set = {1, 2, 3} |
my_frozenset = frozenset([1, 2, 3]) |
Why Use frozenset? (The Main Use Cases)
The immutability of frozenset might seem like a limitation, but it's precisely what makes it useful in specific situations.
As Dictionary Keys
Since dictionary keys must be hashable and immutable, you can't use a regular set as a key. A frozenset is perfect for this.

Imagine you want to create a dictionary where the keys are sets of coordinates.
# This will NOT work!
# regular_set = {(10, 20), (30, 40)}
# location_data = {regular_set: "Home"} # TypeError: unhashable type: 'set'
# This WILL work!
frozen_location = frozenset([(10, 20), (30, 40)])
location_data = {frozen_location: "Home"}
print(location_data)
# Output: {frozenset({(10, 20), (30, 40)}): 'Home'}
# You can now access the value using the same frozenset
print(location_data[frozen_location])
# Output: 'Home'
As Elements in Other Sets
A set can only contain hashable (immutable) elements. Since a set is mutable, you can't have a set of sets. But you can have a set of frozensets.
# This will NOT work!
# set_of_sets = {{1, 2}, {3, 4}} # TypeError: unhashable type: 'set'
# This WILL work!
frozen_set1 = frozenset([1, 2])
frozen_set2 = frozenset([3, 4])
set_of_frozensets = {frozen_set1, frozen_set2}
print(set_of_frozensets)
# Output: {frozenset({1, 2}), frozenset({3, 4})}
When You Need a "Constant" Set
Sometimes you want to define a collection of unique items that should never be modified by accident. Using a frozenset signals to other developers (and to yourself) that this collection is fixed and should not be altered.
# A set of valid user roles that should never change
VALID_ROLES = frozenset(['admin', 'editor', 'viewer', 'guest'])
# A function that checks a user's role
def check_permission(user_role, required_action):
# This is a conceptual check, not a real permission system
if user_role == 'admin':
return True
elif user_role == 'editor' and required_action == 'edit':
return True
# ... etc
return False
# You can safely pass VALID_ROLES around without worrying about it being modified.
print('editor' in VALID_ROLES) # True
# The following would raise an AttributeError, which is good!
# VALID_ROLES.add('moderator')
How to Create and Use frozenset
Creation
You create a frozenset using the frozenset() constructor. It can take any iterable.

# From a list
fs1 = frozenset([1, 2, 2, 3, 4, 4, 5])
print(fs1)
# Output: frozenset({1, 2, 3, 4, 5})
# From a tuple
fs2 = frozenset(('apple', 'banana', 'cherry'))
print(fs2)
# Output: frozenset({'cherry', 'apple', 'banana'})
# From a string (each character becomes an element)
fs3 = frozenset("hello")
print(fs3)
# Output: frozenset({'e', 'h', 'l', 'o'})
# From a regular set
regular_set = {10, 20, 30}
fs4 = frozenset(regular_set)
print(fs4)
# Output: frozenset({10, 20, 30})
Common Operations (Read-Only)
Since frozenset is immutable, you can only perform operations that don't change it. These are the same as for a set, but they return a new frozenset or value instead of modifying the original.
a = frozenset([1, 2, 3, 4])
b = frozenset([3, 4, 5, 6])
# Union (elements in a or b)
union_ab = a.union(b)
print(f"Union: {union_ab}")
# Output: Union: frozenset({1, 2, 3, 4, 5, 6})
# Intersection (elements in both a and b)
intersection_ab = a.intersection(b)
print(f"Intersection: {intersection_ab}")
# Output: Intersection: frozenset({3, 4})
# Difference (elements in a but not in b)
difference_ab = a.difference(b)
print(f"Difference (a - b): {difference_ab}")
# Output: Difference (a - b): frozenset({1, 2})
# Symmetric Difference (elements in a or b but not both)
symmetric_diff_ab = a.symmetric_difference(b)
print(f"Symmetric Difference: {symmetric_diff_ab}")
# Output: Symmetric Difference: frozenset({1, 2, 5, 6})
# Membership testing
print(2 in a) # True
print(5 not in a) # True
# Getting the length
print(len(a)) # 4
# Checking if one is a subset of another
print(a.issubset({1, 2, 3, 4, 5})) # True
What You Can't Do
Any operation that attempts to modify the frozenset will raise an AttributeError.
my_fs = frozenset([1, 2, 3])
# These will all raise an AttributeError!
try:
my_fs.add(4)
except AttributeError as e:
print(f"Error: {e}") # Error: 'frozenset' object has no attribute 'add'
try:
my_fs.remove(2)
except AttributeError as e:
print(f"Error: {e}") # Error: 'frozenset' object has no attribute 'remove'
try:
my_fs.discard(1)
except AttributeError as e:
print(f"Error: {e}") # Error: 'frozenset' object has no attribute 'discard'
try:
my_fs.pop()
except AttributeError as e:
print(f"Error: {e}") # Error: 'frozenset' object has no attribute 'pop'
try:
my_fs.update([4, 5])
except AttributeError as e:
print(f"Error: {e}") # Error: 'frozenset' object has no attribute 'update'
Summary: When to Choose Which?
| Use Case | Best Choice | Why? |
|---|---|---|
| You need a collection of unique items that you will frequently add/remove from. | set |
It's mutable and has methods like .add() and .remove() for efficient modification. |
| You need a collection of unique items that must never change. | frozenset |
Its immutability provides safety and guarantees that the data is constant. |
| You need to use a collection of items as a key in a dictionary. | frozenset |
Dictionary keys must be hashable, and frozenset is, while set is not. |
| You need to store a collection of items inside another set. | frozenset |
Sets can only contain hashable elements, and frozenset fits this requirement. |
In short, use a set for most general-purpose, mutable collections of unique items. Reach for a frozenset when you need immutability, especially for dictionary keys or elements within other sets.
