1

The following code block raises the aforementioned question:

L1 = [1, True, 0, False]*5
L2 = [True, 1, False, 0]*5
D1 = dict(zip(L1, range(len(L1))))
D2 = dict(zip(L2, range(len(L2))))


print(L1) # [1, True, 0, False, 1, True, 0, False, 1, True, 0, False, 1, True, 0, False, 1, True, 0, False]
print(D1) # {1: 17, 0: 19}
print(L2) # [True, 1, False, 0, True, 1, False, 0, True, 1, False, 0, True, 1, False, 0, True, 1, False, 0]
print(D2) # {True: 17, False: 19}

#print(True in D1)
#print(0 in D2)
#print(True == 1)
#print(False == 0)

I can understand that being a subclass of int this is the expected behavior of bool. But does that not affect the structure of list?

How can I handle their (1 and 0 or True and False) explicit presence in any case? i.e. I want something like: {1 : 16, True : 17, 0 : 18, False : 19} (in case of D1), in a pure pythonic way.

Tomerikoo
  • 18,379
  • 16
  • 47
  • 61
ApuCoder
  • 2,795
  • 2
  • 3
  • 19
  • 2
    maybe you could convert them to strings? – CozyCode Dec 11 '21 at 15:59
  • 2
    Does this answer your question? [Is False == 0 and True == 1 an implementation detail or is it guaranteed by the language?](https://stackoverflow.com/questions/2764017/is-false-0-and-true-1-an-implementation-detail-or-is-it-guaranteed-by-the) – Tomerikoo Dec 11 '21 at 15:59
  • What's the use case for distinguishing between the boolean values and their integer counterparts? – chepner Dec 11 '21 at 16:02
  • @chepner an example might be like: to count their number in a list. – ApuCoder Dec 11 '21 at 16:13
  • @Tomerikoo well, I read it, that explains the situation that I know but doesn't show any way about what I want. – ApuCoder Dec 11 '21 at 16:38
  • @CozyCode but that will change their `type` which I want to avoid. – ApuCoder Dec 11 '21 at 16:41
  • 1
    @ApuCoder Part of my question is: why would you be mixing numbers and boolean values in the list in the first place? – chepner Dec 11 '21 at 16:43
  • 3
    Dict keys (or set elements) must be unique. `True` and `1` are not unique, because `True == 1`. (similar: int 1 and float 1.0 are equal) You cannot compare the situation with list/tuple items that have no such limitation. To distinguish you could use a 2-tuple as a key `(type(x),x)` (instead of `x`) – VPfB Dec 11 '21 at 17:21

1 Answers1

1

Gathering ideas from the comments* to an answer:


You seem to be aware of it, but the reason this happens is because True == 1 and False == 0. This means that as dict keys they will be mapped as equal and there can be no repeating keys.

To overcome this:

  1. You could convert the keys to strings:

    L1 = [1, True, 0, False]*5
    D1 = dict(zip(map(str, L1), range(len(L1))))
    
    print(D1)
    

    Will give:

    {'1': 16, 'True': 17, '0': 18, 'False': 19}
    

    But this has the downside of not being able to to actually tell the type of the key. You could overcome this by using ast.literal_eval when reading the dict:

    import ast
    
    for key, value in D1.items():
        key = ast.literal_eval(key)
        print(key, type(key))
    

    Will give:

    1 <class 'int'>
    True <class 'bool'>
    0 <class 'int'>
    False <class 'bool'>
    

  1. Change the keys of the dict to be tuples of the value and its type:

    L1 = [1, True, 0, False]*5
    D1 = dict(zip(zip(L1, [type(x) for x in L1]), range(len(L1))))
    
    print(D1)
    

    Will give:

    {(1, <class 'int'>): 16, (True, <class 'bool'>): 17, (0, <class 'int'>): 18, (False, <class 'bool'>): 19}
    

    And when reading the dict you can get the 0-index element of the keys to get the actual real value.


* converting to string idea by CozyCode. Adding the type to the key idea by VPfB

Tomerikoo
  • 18,379
  • 16
  • 47
  • 61