How come I can add the boolean value False but not True in a set in Python?

Question:

I just started investigating the set data type in Python. For some reason, whenever I add the Boolean value of True to a set it doesn’t appear. However, if I add False to a set it will become an element of the set. I was shocked when I googled this question that nothing came up.

example1 = {1, 2, 7, False}
example2 = {7, 2, 4, 1, True}

print(example1)
print(example2)

The output is:

{False, 1, 2, 7}
{1, 2, 4, 7}
Asked By: DJ Poland

||

Answers:

Because in Python 1 == True (and hash(1) == hash(True)) and you have 1 in your set already.

Imagine this example:

example1 = {0, False, None}
example2 = {1, True}

print(example1)
print(example2)

Will output:

{0, None}
{1}

First set has 0 and None because 0 == False but 0 != None. With second set 1 == True so True isn’t added to the set.

Answered By: Andrej Kesely

False and True are equal to 0 and 1, respectively. They are distinct entities, yet the two equal values cannot both be in a set. This is clearly undesired behavior, yet it is not clear it can be fixed and still allow multiplying by a boolean value to work as documented.

IPython 6.2.1 -- An enhanced Interactive Python.

1 is True
Out[1]: False

{1,True}
Out[2]: {1}

{0,False}
Out[3]: {0}

{False, 0}
Out[4]: {False}

{True, 1}
Out[5]: {True}

Notice that depending on the order of putting them into the set, 1 will not be in the set if True is already in it, and True will not be in the set if 1 is already in it.

Answered By: Bit Chaser

The reason that you are losing boolean values from a set if they already contain 0 or 1 is because the following behavior…

>>> hash(1) == hash(True)
True
>>> hash(0) == hash(False)
True
>>> 1 == True
>>> True
>>> 0 == False
>>> True

is guaranteed in Python 3.x.

Which means that you cannot have both in a set:

>>> set([True, 1])
{True}
>>> set([False, 0])
{False}

The hashes being equal is just as important as the objects being equal, because objects that are “equal” can produce different hashes and vice versa:

class Foo:
    def __init__(self, x): self.x = x
    def __hash__(self): return 1
    def __eq__(self, other): return self.x == other.x

class Bar:
    def __init__(self, x): self.x = x
    def __hash__(self): return 2
    def __eq__(self, other): return self.x == other.x

>>> x = Foo(3)    
>>> y = Bar(3)
>>> x == y
True
>>> hash(x) == hash(y)
False
>>> set([x, y])
{<__main__.Bar at 0x56ed278>, <__main__.Foo at 0x5707390>}

You can also have a set that contains items with the same hashes, if those items are not equal:

>>> hash('a')
-904409032991049157
>>> hash(-904409032991049157)
-904409032991049157
>>> hash('a') == hash(-904409032991049157)
True
>>> set(['a', -904409032991049157])
{-904409032991049157, 'a'}

This behavior is not guaranteed in Python 2.x, for the simple reason that True and False are not reserved keywords (this change was introduced in 3.x). You may reassign them (although better not to), so there is no reason that the same behavior must hold in Python 2.x:

>>> True = 5
>>> hash(True) == hash(1)
False
>>> set([1, True])
set([1, 5])

But don’t let the fact that True was replaced with 5 discourage you! We can abuse the representation of a class to make it appear as though True really is in the set:

class Foo(object):
    def __repr__(self):
        return('True')

>>> True = Foo()
>>> set([1, True])
set([1, True])

Obviously the last couple code snippets are bad practice, and are only for demonstration. The main takeaway is that equal objects with the same hash cannot be contained in the same set, and in Python 3.x, 1 and True, and 0 and False, will always have the same hash, and will always be equal.

Answered By: user3483203
Categories: questions Tags: ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.