9

I cannot add the integer number 1 to an existing set. In an interactive shell, this is what I am doing:

>>> st = {'a', True, 'Vanilla'}
>>> st
{'a', True, 'Vanilla'}
>>> st.add(1)
>>> st
{'a', True, 'Vanilla'}   # Here's the problem; there's no 1, but anything else works
>>> st.add(2)
>>> st
{'a', True, 'Vanilla', 2}

This question was posted two months ago, but I believe it was misunderstood. I am using Python 3.2.3.

Gareth Latty
  • 86,389
  • 17
  • 178
  • 183
jiveturkey
  • 2,484
  • 1
  • 23
  • 41

5 Answers5

13
>>> 1 == True
True

I believe your problem is that 1 and True are the same value, so 1 is "already in the set".

>>> st
{'a', True, 'Vanilla'}
>>> 1 in st
True

In mathematical operations True is itself treated as 1:

>>> 5 + True
6
>>> True * 2
2
>>> 3. / (True + True)
1.5

Though True is a bool and 1 is an int:

>>> type(True)
<class 'bool'>
>>> type(1)
<class 'int'>

Because 1 in st returns True, I think you shouldn't have any problems with it. It is a very strange result though. If you're interested in further reading, @Lattyware points to PEP 285 which explains this issue in depth.

Nolen Royalty
  • 18,415
  • 4
  • 40
  • 50
  • 1
    Interesting that `1` and `True` are considered equal when their representation is totally different. – Mark Ransom May 02 '12 at 19:02
  • 1
    @MarkRansom I agree, I'd almost call this a flaw? I would love to hear Guido's explanation of why this happens. – Nolen Royalty May 02 '12 at 19:03
  • I suppose it's no different than `1 == 1.0`. It sure looks odd though. – Mark Ransom May 02 '12 at 19:04
  • 3
    See http://stackoverflow.com/questions/8169001/why-is-bool-a-subclass-of-int/8169072#8169072 – Steven Rumbalski May 02 '12 at 19:05
  • 3
    It's a long-disputed issue - a lot of people wanted it changed for Python 3. Originally there was no separate `bool` type in Python. Read [PEP 285](http://www.python.org/dev/peps/pep-0285/) for why it was done like this. – Gareth Latty May 02 '12 at 19:06
  • @StevenRumbalski Could you please express (maybe in a different answer) what operations happen "under the hood" when we call `1 in {'a',True}`, which eventually return `True`? As for me, it's not very trivial. – ovgolovin May 02 '12 at 19:11
  • @ovgolovin: For historical (note i'm not saying "for logical") reasons, `bool` inherits from `int`. Since `True` has a value of 1, 1 is considered a member of the set `{'a',True}`. – Steven Rumbalski May 02 '12 at 19:16
  • 1
    @StevenRumbalski As I understand, it happens the following way: 1)`hash(1)` is calculated to be equal to `1`; 2)this hash value is searched in the set; 3)the corresponding bin is found; 4) there is a value `True` in that bin which happens to be `1==True`; 5) `1 in {True}` returns `True`. Correct? :) – ovgolovin May 02 '12 at 19:23
  • I also do not like the fact that `1 == True`, because it is simply confusing. However, it is the implementation tradeof, possibly also herigage of the C language. The major argument probably is that makin `True` not equal to `1` would break older Python code. – pepr May 05 '12 at 20:53
3

I believe, though I'm not certain, that because hash(1) == hash(True) and also 1 == True that they are considered the same elements by the set. I don't believe that should be the case, as 1 is True is False, but I believe it explains why you can't add it.

g.d.d.c
  • 46,865
  • 9
  • 101
  • 111
  • The `is` result is an implementation detail and should never be relied upon. `==` is the proper test. – Mark Ransom May 02 '12 at 19:01
  • +1 because the hash equivalency is key here, but I agree with Mark that what you said about `is` isn't really relevant. `1 is 1` could be `False` and not violate anything in documentation (and something like `300 is (299+1)` probably will give you `False`). – Andrew Clark May 02 '12 at 19:05
1

1 is equivalent to True as 1 == True returns true. As a result the insertion of 1 is rejected as a set cannot have duplicates.

codaddict
  • 445,704
  • 82
  • 492
  • 529
0

Here are some link if anyone is interested in further study.

Is it Pythonic to use bools as ints?

https://stackoverflow.com/a/2764099/1355722

Community
  • 1
  • 1
Tahmid Rafi
  • 469
  • 1
  • 6
  • 18
0

We have to use a list if you want to have items with the same hash.If you're absolutely sure your set needs to be able to contain both True and 1.0, I'm pretty sure you'll have to define your own custom class, probably as a thin wrapper around dict. As in many languages, Python's set type is just a thin wrapper around dict where we're only interested in the keys.

Ex:

st = {'a', True, 'Vanilla'}

list_st = []

for i in st:
    list_st.append(i)
    
list_st.append(1)

for i in list_st:
    print(f'The hash of {i} is {hash(i)}')

produces

The hash of True is 1
The hash of Vanilla is -6149594130004184476
The hash of a is 8287428602346617974
The hash of 1 is 1

[Program finished]
Subham
  • 397
  • 1
  • 6
  • 14