-1

I am trying to understand how index operator [] works in some cases involving dictionaries.

>>> {'a': 1, 'b':2}[1]
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
KeyError: 1

However...

>>> {True: 1, False:2, True: 3}[1]
3

What's going on here?

Also, what's with the below?

>>> {True: 1, False:2, True: 3}
{False: 2, True: 3}

Why is {True: 1, False:2, True: 3}" evaluating to "{False: 2, True: 3}"?

When searching for answer, I came across this question, but I couldn't find answers to my questions there.

Thanks.

Glorfindel
  • 21,988
  • 13
  • 81
  • 109
sam
  • 777
  • 2
  • 6
  • 19

2 Answers2

4

{True: 1, False:2, True: 3}1

What's going on here?

This is an extremely specific case, which might be really missguiding. In python "1" is True and "0" is False, thus you can use these integers/floats as boolean keys. However, it is not a regular casting operation, so if you try to use "1.1" as a key, it will not be casted to True, even though bool(1.2)==True. So in general - you have to index with the actual keys. The fact that True is actually 1 "behind the scenes" and False is 0 is a specific exception and you should not write code depending on this behaviour.

d = {True: 'test', False: 'test'}
print d[True] # works, valid use
print d[False] # works, valid use
print d[1] # works due to Python specifics, do not use
print d[0] # works due to Python specifics, do not use
print d[1.0] # works due to Python specifics, do not use
print d[0.0] # works due to Python specifics, do not use
print d[1.1] # raises exception, as it should
print d[''] # raises exception, as it should

This is a consequence of how boolean type is defined in Python

Boolean values are the two constant objects False and True. They are used to represent truth values (although other values can also be considered false or true). In numeric contexts (for example when used as the argument to an arithmetic operator), they behave like the integers 0 and 1, respectively. The built-in function bool() can be used to cast any value to a Boolean, if the value can be interpreted as a truth value (see section Truth Value Testing above).

Thus showing why 1==True and 0==False.

More precisely, Python dictionaries are hash tables, so in order to find a given key one computes its hash (through hash(key)), which points to a given "bucket" and then iteratively checks in that bucker whether key equals to what is in that bucket. And since in Python hash(1) == hash(1.0) == hash(True) and also 1 == 1.0 == True, they will work as the same key. On the other hand hash(1.1) != hash(1.0) so it will not be the same. This shows why it is not casting, it is about equality and hashing.

Why is {True: 1, False:2, True: 3}" evaluating to "{False: 2, True: 3}"?

Its because in a dictionary you can only have one value for each key, you tried to assign two different values to a key "True" and only one got assigned.

Community
  • 1
  • 1
lejlot
  • 64,777
  • 8
  • 131
  • 164
  • Dictionaries are not ordered (prior to Cpython 3.6) but they are evaluated left to right, in this sense it is guaranteed that the last key with `True` will be the one kept – Chris_Rands Jan 30 '17 at 22:45
  • @Chris - do you know the language standard specification that describes how these objects are being parsed? Are they always just left to right assignments? I was unable to find the doc thus the note, as it seems to be interpreter specific, not language specific. – lejlot Jan 30 '17 at 22:50
  • Thank you lejlot and @Chris_Rands. Totally mistook [] for indexing, without noticing that it's a dictionary. That tripped me. – sam Jan 31 '17 at 00:56
  • 1
    The keys are updated left to right, this is guaranteed I'm pretty sure. Although you can't do this with keywords, like `dict(a=1,a=2)` raises a `SyntaxError` – Chris_Rands Jan 31 '17 at 08:52
-1

Python dict is a map from key to a value, which maps "hashable" values to arbitrary value objects, you can only have one value per key.

In this case your example

>>> {True: 1, False:2, True: 3}
{False: 2, True: 3}

Is pretty much the same as this

>>> {"a": 1, "b": 2, "a": 3}
{'a': 3, 'b': 2}

Where the only one True, or "a" value is kept.

The access via [] is not an index in a dict, it's referencing via the key. I.e. you could write something like this

>>> my_map = {"a": 1, "b": 2}
>>> my_map["b"]
2

Why 1 works instead of True is because of how booleans have been defined to wkr as the integer values 1 and 0 as @lejlot explains, you should avoid relying on such things whenever possible.

Janne Enberg
  • 1,908
  • 1
  • 14
  • 12
  • Python does not have "automatic casting", this is actually one of the Python mantras/assumptions. No implicit type conversions. – lejlot Jan 30 '17 at 22:59
  • Thanks @Janne. otally mistook [] for indexing, without noticing that it's a dictionary. That tripped me. – sam Jan 31 '17 at 00:58
  • Yes @lejlot, quite true, Python does not implicitly cast things, however there are a few surprises with that such as in this case. Automatic casting was poorly chosen wording to try and simplify the concept. – Janne Enberg Feb 01 '17 at 07:01