pylearner
pylearner

Reputation: 765

TypeError : Unhashable type

I am trying to get a list of list of tuples : something like [ [(1,0),(2,0),(3,0)],[(1,1),(2,1),(3,1)....]] I used this statement

set([(a,b)for a in range(3)]for b in range(3))

But it gives me an error

TypeError: unhashable type: 'list'

I have 2 questions for the Python Guru's:

a) When I look at the Python definition of Hashable -

"An object is hashable if it has a hash value which never changes during its lifetime (it needs a hash() method)"

when I used dir function on the expression above

dir([(a,b)for a in range(3)]for b in range(3))

it seems to say the __hash__ is there. So, why do I get the error?

I was able to get [[(0, 0), (1, 0), (2, 0)], [(0, 1), (1, 1), (2, 1)], [(0, 2), (1, 2), (2, 2)]] by using the list command :

list(list((a,b) for a in range(3)) for bin range(3))

b)list and set both takes Iterable as parameter. How come one works(list) and another doesn't (set)?

Upvotes: 64

Views: 308702

Answers (6)

utdemir
utdemir

Reputation: 27216

You are creating a set via the set(...) call, and set needs hashable items. You can't have set of lists. Because lists aren't hashable.

[[(a,b) for a in range(3)] for b in range(3)] is a list. It's not a hashable type. The __hash__ you saw in dir(...) isn't a method, it's just None``.

A list comprehension returns a list, you don't need to explicitly use list there, just use:

>>> [[(a,b) for a in range(3)] for b in range(3)]
[[(0, 0), (1, 0), (2, 0)], [(0, 1), (1, 1), (2, 1)], [(0, 2), (1, 2), (2, 2)]]

Try those:

>>> a = {1, 2, 3}
>>> b= [1, 2, 3]
>>> type(a)
<class 'set'>
>>> type(b)
<class 'list'>
>>> {1, 2, []}
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: unhashable type: 'list'
>>> print([].__hash__)
None
>>> [[],[],[]] #list of lists
[[], [], []]
>>> {[], [], []} #set of lists
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: unhashable type: 'list'

Upvotes: 41

Guillaume Chevalier
Guillaume Chevalier

Reputation: 10888

TLDR:

- You can't hash a list, a set, nor a dict to put that into sets

- You can hash a tuple to put it into a set.

Example:

>>> {1, 2, [3, 4]}
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: unhashable type: 'list'

>>> {1, 2, (3, 4)}
set([1, 2, (3, 4)])

Note that hashing is somehow recursive and the above holds true for nested items:

>>> {1, 2, 3, (4, [2, 3])}
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: unhashable type: 'list'

Dict keys also are hashable, so the above holds for dict keys too.

Upvotes: 26

schlamar
schlamar

Reputation: 9511

The real reason because set does not work is the fact, that it uses the hash function to distinguish different values. This means that sets only allows hashable objects. Why a list is not hashable is already pointed out.

Upvotes: 9

fransua
fransua

Reputation: 1608

... and so you should do something like this:

set(tuple ((a,b) for a in range(3)) for b in range(3))

... and if needed convert back to list

Upvotes: 7

multipleinterfaces
multipleinterfaces

Reputation: 9163

You'll find that instances of list do not provide a __hash__ --rather, that attribute of each list is actually None (try print [].__hash__). Thus, list is unhashable.

The reason your code works with list and not set is because set constructs a single set of items without duplicates, whereas a list can contain arbitrary data.

Upvotes: 2

Mark Ransom
Mark Ransom

Reputation: 308111

A list is unhashable because its contents can change over its lifetime. You can update an item contained in the list at any time.

A list doesn't use a hash for indexing, so it isn't restricted to hashable items.

Upvotes: 14

Related Questions