Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Dictionary coercion intentional or no?

If I write:

d = { 0:'a', 1:'b' }
d[False] = 'c'
d[True] = 'd'
print(d)

I get:

{ 0:'c', 1:'d' }

Why is it coercing them into ints? It does the same thing in reverse.

d = {False:'a', True:'b'}
d[0] = 'c'
d[1] = 'd'
print(d)

Output is:

{False:'c', True: 'd'}

Can this be disabled? Is it a bug?

like image 615
temporary_user_name Avatar asked Nov 08 '13 03:11

temporary_user_name


2 Answers

It's because those values are considered equal:

>>> True == 1
True
>>> 
>>> False == 0
True

and have the same hash values:

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

Therefore, from the dictionary's point of view, True and 1 are indistinguishable as are False and 0.

There is no way to "disable" this -- you shouldn't be using non-homogenous keys in a dict to begin with.

A potential workaround in this specific case would be to reserve special int values for True and False other than 1 and 0, respectively (presuming you need 1 and 0 as independent keys). For instance, you could have -1 represent True and -2 represent False.

like image 145
arshajii Avatar answered Nov 07 '22 06:11

arshajii


Just offering some background on arshajii's answer.

The two boolean values, True and False, have a strange relation with integers.

On one hand, they have different string representations, and have separate identities:

>>> print(True)
True
>>> print(1)
1

>>> True is 1
False

On the other hand, they behave as integers under comparisons and arithmetic:

>>> True == 1
True
>>> True + 1
2

The reason for this behavior is compatibility. A long time ago, the bool type didn't exist. "Boolean" operators copied C behavior, reusing 0 and 1 for "false" and "true".

Eventually Guido realized this didn't make much sense, and added the constants we know and love.

But there was a problem. Even then, there was already a lot of code that treated boolean values like integers. If boolean operations started using the "correct" type, all this code would break.

So Guido made a compromise. Booleans have their own type, bool, and display differently to integers. But in arithmetic operations and comparisons, most notably __eq__ and __hash__, they are treated as one and the same. So old code would continue to work, while new code can still take advantage of the new bool type.

Maybe that'll change in Python 4. But for now, bool is a subclass of int, and we'll have to live with that.

(On a related note, that's one of the reasons why True and False are in Title Case, rather than lower case like other Python keywords.)

like image 35
Lambda Fairy Avatar answered Nov 07 '22 06:11

Lambda Fairy