60

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)?

Neil G
  • 32,138
  • 39
  • 156
  • 257
pylearner
  • 725
  • 1
  • 5
  • 10

6 Answers6

38

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'
Michael Mior
  • 28,107
  • 9
  • 89
  • 113
utdemir
  • 26,532
  • 10
  • 62
  • 81
23

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.

Guillaume Chevalier
  • 9,613
  • 8
  • 51
  • 79
  • I'm working with lists of lists, so this is actually very useful. Thanks. – NWMT Aug 23 '16 at 16:22
  • creating a tuple representation of a list is faster than a string representation via e.g. `repr( [1,2,3] )`, right? What about nested lists? – Nil Feb 05 '17 at 20:18
  • 1
    @AlexandrNil for nested lists you could use comprehensions, i.e., for a 2-dimensional list `a = [[1,2],[3,4]] # a 2 by 2 matrix` can be transformed via `a = set(tuple(tuple(x) for x in a)))` – AlexeyGy May 16 '19 at 08:40
14

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.

Mark Ransom
  • 299,747
  • 42
  • 398
  • 622
9

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.

schlamar
  • 9,238
  • 3
  • 38
  • 76
7

... 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

fransua
  • 1,559
  • 13
  • 30
2

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.

multipleinterfaces
  • 8,913
  • 4
  • 30
  • 34