Join us and get access to thousands of tutorials and a community of expert Pythonistas.

Unlock This Lesson

This lesson is for members only. Join us and get access to thousands of tutorials and a community of expert Pythonistas.

Unlock This Lesson

Frozen Sets

00:00 Let’s talk about frozen sets, which are exactly the same as regular sets but are immutable and, by definition, hashable. Let’s first define a regular set and see how the syntax is almost the same. So here we have y. It contains three elements, {'bar', 'baz', 'foo'}.

00:16 Let’s just do Up arrow, create a new variable x, put the word frozen in front of the word set(), and now we have a frozenset.

00:26 You can operate on a frozenset just like how you would on a regular set, so we can even get the length, we can iterate through our frozenset.

00:36 We can also use operators such as intersection (&). So here, we can actually intersect our frozenset with a regular set and get a frozenset.

00:46 So this would get the intersection of our set {'foo', 'bar', 'baz'} with a set {'baz'}, and it just contains 'baz'. We can do other things like difference (-).

00:56 Let me clear the output. But, as I mentioned before, the one difference is that frozen sets are immutable. That means we cannot mutate them. So normally, we can do something like x.add() and it errors. It says 'frozenset' object has no attribute 'add'.

01:13 We can’t clear it.

01:16 We can’t pop anything. We can’t do anything like that.

01:21 And x stayed the same. This is useful because many objects and data structures like sets can only contain hashable objects. So we can’t have a set of sets, right?

01:32 That isn’t allowed. We can’t have a set with a set in it—unhashable type: 'set'but we could have a set with a frozenset inside of it.

01:40 So this is sort of the idea of having sets of sets, except the sets inside of our set can’t be changed. So we could change our actual set, we can add a frozenset to another set, and then now x has two frozensets.

01:58 So notice how I didn’t actually mutate the set inside of x, but I just mutated x itself to add a frozenset.

02:07 This is also very useful if, let’s say, you want to use a set as a key in a dictionary. Well, normally we can’t do that,

02:18 but we can do frozenset().

02:22 So sometimes this is useful because we could do something like dict.keys(), we can do for key in d.keys(): and then print the key.

02:32 So, we actually can maybe say print(1 in key). And that’s True, because 1 exists in the key of our dictionary, which is a frozenset. This is not something we can do with regular sets.

jckichuk on July 10, 2020

Hey James, Is there a reason to use frozen sets for dictionary keys instead of a singleton tuple? Or is it just preference?

James Uejio RP Team on July 11, 2020

Hi jckichuck, frozen sets have the benefit of instant lookup and also other built in set methods, while tuples do not. However in the real world, it’s a very small optimization and you’ll probably see tuples as keys in most cases.

jckichuk on July 14, 2020

Makes sense. Thank you!

Cindy on March 1, 2022

Hi James, as frozenset is immutable, I am wondering why it can still be operated with regular set, for example: frozenset ({'bar','baz','foo'})&{'baz'}? And in terms of the difference between ‘immutable’ and ‘hashable’, it is said that hashable object also contains mutable object. In addition to applying hash(s) function, is there any other way we can tell if a mutable object is hashable? From literal meaning, they seem to be the same. Thank you.

Bartosz Zaczyński RP Team on March 2, 2022

@Cindy An immutable object can still support all mutable operations without violating the immutability. The trick is to always return a copy of the original object instead of mutating it in place. This is true for other immutable data types in Python, such as strings and numbers.

Notice how the original frozenset remains unaffected by the intersection (&):

>>> fruits = frozenset({"apple", "banana", "orange"})
>>> fruits & {"apple"}
frozenset({'apple'})
>>> fruits
frozenset({'apple', 'orange', 'banana'})

Hashability is very closely related to immutability, and in most cases, you can almost think of them as synonyms. However, you can most definitely create all combinations of mutable/immutable and hashable/not hashable objects, for instance:

>>> class MutableHashable:
...     def __init__(self, value):
...         self.value = value
...     
...     def __hash__(self):
...         return hash(self.value)
... 
>>> obj = MutableHashable("foo")
>>> hash(obj)
4154253771125308579
>>> obj.value = "bar"
>>> hash(obj)
70073152734057689

>>> class ImmutableNotHashable:
... 
...     __hash__ = None
...     
...     def __init__(self, value):
...         self._value = value
... 
>>> obj = ImmutableNotHashable("foo")
>>> hash(obj)
Traceback (most recent call last):
  File "<input>", line 1, in <module>
    hash(obj)
TypeError: unhashable type: 'ImmutableNotHashable'

Stay tuned for an upcoming tutorial about implementing a hash table, which will dive into precisely this topic in much more depth 😉

Become a Member to join the conversation.