Loading video player…

Immutable vs. Hashable

00:00 Immutable objects are a type of object that cannot be modified after they were created. Hashable objects, on the other hand, are a type of object that you can call hash() on.

00:11 So if you go into the Python interpreter and type hash, open parenthesis, and then put your object in there, close , and hit Enter and it does not error, then that means that your object is hashable.

00:23 All immutable objects are hashable, but not all hashable objects are immutable. That’s just a rule, so anytime you see some example immutable object, you know for a fact that it’s hashable, but there are some cases where there are hashable objects that you actually can mutate. Python sets can only include hashable objects.

00:43 That means that they can include immutable objects because all immutable objects are hashable and they can include mutable objects that are hashable. So, some examples that you’ve probably seen of immutable objects are tuples, strings, integers, and Booleans.

01:00 This is because these objects cannot be modified after they were created.

01:05 And then hashable objects sort of encompasses all immutable objects. An example would be you could define a class and then define your own built-in .__hash__() method, and this would mean that your object is hashable.

01:18 If you don’t know this syntax, that’s totally fine. You can also imagine if you can call hash() on your object and it doesn’t error, then it’s hashable. Lists and dictionaries are unhashable because we cannot call the hash() method on them. Let’s see some examples.

01:35 Let’s look at strings. We can create a string like this, and we can see that strings are indeed immutable. Let’s try to actually mutate it and see that this will error.

01:46 So if we try to add a character to the end or change the first character to another character, both of those will error. That’s because strings are immutable and do not actually have any methods that allow us to mutate them.

01:59 The same goes for integers. We can create an integer, and we can’t add a number to an integer or add an integer to the end of itself and actually change what this 10 is.

02:11 Once the 10 is created, we cannot change it. We can, however, reassign xbut that doesn’t actually change the 10.

02:18 All that does is just change what x is bound to, and then the 10 just goes away. We can verify that strings and integers are both hashable by calling the hash() method on them. So hash(s) will not error, it’ll spit out a number. And this number just represents the hash of s and you can imagine hashing just changes the object into a number.

02:40 And each time we hash it, we get that same value. hash(x) gives us the integer back. hash() on Booleans and tuples all don’t error. However, we saw that lists and dictionaries are unhashable, which means that calling hash() on them errors and says unhashable type: 'list'.

03:01 The same goes for dictionaries, unhashable type: 'dict'.

03:07 So now that you know what immutable and hashable mean, let’s look at how we can define sets.

Avatar image for Zarata

Zarata on April 6, 2020

I’m fairly certain you’re trying to avoid going down a rabbit-hole complexity, but for me, the definition of “hashable” simply as “a type of object that you can call hash() on” feels incomplete, and almost circular. Sorry. Maybe you should add “beyond that, there be dragons here”?

Avatar image for James Uejio

James Uejio RP Team on April 11, 2020

Hi @Zarata yes good point I tried to simplify it in the video because this course is aimed to not require too much prior knowledge. Here is a good stack overflow answer for anyone who is curious what “Hashable” means: stackoverflow.com/questions/14535730/what-does-hashable-mean-in-python

Avatar image for joefol

joefol on June 24, 2020

Hi James, I started to watch your video today in the daytime and was fine to follow your video in the console. Now I’m picking up where I left of and its night time and I’m struggling to work it out (the blue on the black is impossible to work out) with the night light on, maybe a lighter background would help. Thanks

Avatar image for kiran

kiran on July 25, 2020

all immutable objects are hashable. but not all hashable objects are immutable.

can you give example for this statement.?

Avatar image for Bartosz Zaczyński

Bartosz Zaczyński RP Team on July 28, 2020

When you talk about Python’s built-in data types, then most of the immutable ones are hashable. These include tuples or frozen sets, for example:

# Immutable and hashable:
>>> hash(frozenset(['apple', 'banana', 'orange']))
-501384979540254233

It allows objects of these types to become dictionary keys or set members. On the other hand, instances of built-in mutable types, such as lists, dicts, or sets, are never hashable:

# Mutable but not hashable:
>>> hash(set(['apple', 'banana', 'orange']))
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: unhashable type: 'set'

These can’t be used as dictionary keys, nor can they be added to a set, which both use the hash() function to calculate elements’ location in memory.

However, since the hash is derived from the object’s internal value, sometimes, even the immutable data types won’t be hashable in Python. When you add a mutable element, like a list, to an immutable collection, its collective value will no longer be immutable. Therefore, it won’t be hashable:

# Immutable but not hashable:
>>> hash(('apple', 'banana', 'orange', ['a list']))
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: unhashable type: 'list'

The tuple itself has a fixed set of elements, but one of them is mutable and can be modified without touching the tuple it belongs to.

To answer your question, I’m not aware of any built-in data types that are mutable and hashable at the same time, but user-defined classes have such properties by default:

>>> class MutableAndHashable:
...     def __init__(self, value):
...         self.value = value
...
>>> instance = MutableAndHashable(42)
>>> hash(instance)
8789535480438
>>> instance.value = 24
>>> hash(instance)
8789535480438
>>> hash(instance) == id(instance)
True

The default hash value of a class instance is the same as its identity. To disable “hashability” of a class, you can specify the __eq__() magic method that compares two instances:

>>> class MutableButNotHashable:
... 
...     def __init__(self, value):
...         self.value = value
...     
...     def __eq__(self, other):
...         if type(self) is type(other):
...             return self.value == obj.value
...         return False
... 
>>> instance = MutableButNotHashable(42)
>>> hash(instance)
Traceback (most recent call last):
  File "<input>", line 1, in <module>
    hash(instance)
TypeError: unhashable type: 'MutableButNotHashable'

However, when you override the __eq__() method, you almost certainly want to define the __hash__() one as well to adhere to the hash/eq contract. Otherwise, Python won’t use the default hash implementation anymore.

Also, a good hash function needs to have specific properties, such as avoiding so-called “collisions” or distributing elements evenly. But you don’t need to worry about it as long as you delegate to the built-in hash() function.

I hope this clears things up for you.

Avatar image for kiran

kiran on July 29, 2020

@Bartosz Zaczyński thanks for the update.

Become a Member to join the conversation.