0

I test a piece of code as follows:

>>>s = set(([1, (2, 3)])) #OK                  # 1
>>>s
{(1, (2, 3))} # put a tuple as set element

>>>s = set([(1, [2, 3])]) #error               # 2
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: unhashable type: 'list'

>>>s = set([[1, 2]]) #error                    # 3
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: unhashable type: 'list'

When I put a tuple as a set element (in 1), it works. But if I put a tuple containing a list as an element (in 2) or just put a list (in 3) in a set element, it went wrong. I know coding like this: list = [1,2]; s = set([list]) is not allowed because we can change list later. But I just don't understand, since [1,2] itself is immutable and won't change, why I can't code s = set([[1,2]]).

CrazyChucky
  • 3,263
  • 4
  • 11
  • 25
Keino
  • 11
  • 1
  • 3
    "while[1,2] itself is immutable " No, this list is mutable and can be changed externally. – Code-Apprentice Aug 01 '20 at 15:55
  • 2
    Double check the output you claim from each step. I get something different for the first example: https://repl.it/@codeguru/GrimAccomplishedLocks#main.py. Specifically, the set now has two elements: an integer and a tuple. But your output shows the set contains a single tuple which in turn consists of an int and a tuple. – Code-Apprentice Aug 01 '20 at 15:58
  • @Code-Apprentice thanks for the reminder, and I changed the 1 from`s = set([1,(2,3))]` to `s = set([(1,(2,3))])` – Keino Aug 01 '20 at 16:44
  • that amount of nesting just adds unnecessary complexity to your question. I think the original code was a better example as long as you show the correct output. – Code-Apprentice Aug 01 '20 at 22:24

2 Answers2

0

Notice that the error message says "unhashable type: 'list'". This is different than immutable vs mutable. Let's look at a simpler example that causes the same error:

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

So here we see that we can add an integer 1 to the set but not a list [2, 3] because the list is not hashable. This has to do with how set stores and retrieves values. The set class requires that it can call hash() on its elements.

Now your version:

>>>s = set([1,(2,3)]) #OK                   1
>>>s
{1,(2.3)} # Note: the output in your original question has an extra set of parentheses

When you call set() like this, it adds the elements of the given list to the set. The end result is the same as calling add() on each element.

Now if we try:

>>>s = set([1,[2,3]])

We shouldn't be surprised by the error because we are attempting to add a list to the set.

Code-Apprentice
  • 81,660
  • 23
  • 145
  • 268
  • Are there any `mutable` objects that are `hashable`? – Sayandip Dutta Aug 01 '20 at 16:18
  • @Code-Apprentice Did you mean to `s.add([2, 3])` twice? You show the second time throwing the appropriate error, but the first time failing silently. – CrazyChucky Aug 01 '20 at 16:21
  • 1
    @SayandipDutta Not builtins, no, though it is possible to define your own. See this answer: https://stackoverflow.com/a/2671476/12975140 – CrazyChucky Aug 01 '20 at 16:25
  • @Code-Apprentice also Keino edited their post. It appears they actually created the first set as `set(([1, (2, 3)]))`, so that it contains the tuple as a single element. – CrazyChucky Aug 01 '20 at 16:29
  • thanks for your answer, a little confused, why a definite list `[2,3]` is not hashable?(sorry I'm new to data sttructure) – Keino Aug 01 '20 at 16:30
  • 1
    @Keino No list is hashable, whether it has constant elements or not. – Code-Apprentice Aug 01 '20 at 16:36
  • 1
    @Keino While "hashable" and "mutable" are not the *same* thing, it's easy to remember that none of Python's builtin mutable types are hashable. `list` is a mutable builtin type, so you know it isn't hashable. – CrazyChucky Aug 01 '20 at 16:37
  • @CrazyChucky thanks a lot, easy to remember, mybe I should learn more about `hash` – Keino Aug 01 '20 at 16:48
0

You can't insert mutable objects into set, just as you can't use them as dict key.

You can use there only immutable objects due to internal implementation which uses hash(obj) to identify the objects you put in. If you were able to insert mutable objects, they could change the hash(obj) value which would make the set, dict, etc behave in unexpected incorrect ways.

[1, 2] even if entered as list literal is still a list and thus mutable object.

Consider following:

x = set([[1, 2]])
next(iter(x)).append(3)
Jan Matějka
  • 1,880
  • 1
  • 14
  • 31
  • 1
    "`[1, 2]` even if entered as list literal is still a list and thus mutable object.", that's what I confused at, and maybe I should check more information, thanks. – Keino Aug 01 '20 at 16:57
  • @Code-Apprentice It is a counter-example to OP's assumption that list-literals are technically or practically immutable – Jan Matějka Aug 12 '20 at 14:42
  • @JanMatějka You should explain that in more detail in your answer. – Code-Apprentice Aug 12 '20 at 15:35