By using this site, you agree to our updated Privacy Policy and our Terms of Use. Manage your Cookies Settings.
455,587 Members | 1,731 Online
Bytes IT Community
+ Ask a Question
Need help? Post your question and get tips & solutions from a community of 455,587 IT Pros & Developers. It's quick & easy.

False and 0 in the same dictionary

P: n/a
I've been using Python for a while (4 years) so I feel like a moron
writing this post because I think I should know the answer to this
question:

How do I make a dictionary which has distinct key-value pairs for 0,
False, 1 and True.
As I have learnt, 0 and False both hash to the same value (same for 1
and True).
>>b = {0:'xyz', False:'abc'}
b
{0: 'abc'} # Am I the only one who thinks this is weird?

This obviously stems from the fact that 0 == False but 0 is not False
etc. etc.

That doesn't help my case where I need to distinguish between the two

The same issue applies in a list:

Suppose I do:
>>a = [0, 1, True, False]
a.index(False)
0

Wha??? Help.
Nov 4 '08 #1
Share this Question
Share on Google+
14 Replies


P: n/a
Prateek <su*****@gmail.comwrote:
I've been using Python for a while (4 years) so I feel like a moron
writing this post because I think I should know the answer to this
question:

How do I make a dictionary which has distinct key-value pairs for 0,
False, 1 and True.
How about using (x, type(x)) as the key instead of just x?

Nov 4 '08 #2

P: n/a
On Nov 5, 1:52*am, Duncan Booth <duncan.bo...@invalid.invalidwrote:
Prateek <sure...@gmail.comwrote:
I've been using Python for a while (4 years) so I feel like a moron
writing this post because I think I should know the answer to this
question:
How do I make a dictionary which has distinct key-value pairs for 0,
False, 1 and True.

How about using (x, type(x)) as the key instead of just x?
Yup. I thought of that. Although it seems kinda unpythonic to do so.
Especially since the dictionary is basically a cache mostly containing
strings. Adding all the memory overhead for the extra tuples seems
like a waste just for those four keys.

Is there a better way?
I also thought of using a custom __eq__ method in a custom class
which extends the dict type but decided that was even worse.

Prateek
Nov 4 '08 #3

P: n/a
On Nov 4, 4:21*pm, Prateek <sure...@gmail.comwrote:
On Nov 5, 1:52*am, Duncan Booth <duncan.bo...@invalid.invalidwrote:
Prateek <sure...@gmail.comwrote:
I've been using Python for a while (4 years) so I feel like a moron
writing this post because I think I should know the answer to this
question:
How do I make a dictionary which has distinct key-value pairs for 0,
False, 1 and True.
How about using (x, type(x)) as the key instead of just x?

Yup. I thought of that. Although it seems kinda unpythonic to do so.
Especially since the dictionary is basically a cache mostly containing
strings. Adding all the memory overhead for the extra tuples seems
like a waste just for those four keys.

Is there a better way?
I also thought of using a custom __eq__ *method in a custom class
which extends the dict type but decided that was even worse.

Prateek
Hmm, my original reply didn't show up.

I'm curious as to what you're trying to accomplish.

Bear in mind that I type this response not knowing your application.
While Python is not a statically typed language, 0 and False are
essentially different types (int and bool). Storing them both as keys
of a dictionary just doesn't seem like a good design.
Nov 4 '08 #4

P: n/a
Prateek <su*****@gmail.comwrites:
On Nov 5, 1:52*am, Duncan Booth <duncan.bo...@invalid.invalidwrote:
>Prateek <sure...@gmail.comwrote:
I've been using Python for a while (4 years) so I feel like a moron
writing this post because I think I should know the answer to this
question:
How do I make a dictionary which has distinct key-value pairs for 0,
False, 1 and True.

How about using (x, type(x)) as the key instead of just x?

Yup. I thought of that. Although it seems kinda unpythonic to do so.
Especially since the dictionary is basically a cache mostly containing
strings. Adding all the memory overhead for the extra tuples seems
like a waste just for those four keys.

Is there a better way?
I also thought of using a custom __eq__ method in a custom class
which extends the dict type but decided that was even worse.

Prateek
You could use currying on Duncan's solution :). It would
give something like this (minimal implementation):

from collections import defaultdict

class DictByType(object):
def __init__(self):
self.dicts = defaultdict(dict)
def __setitem__(self, key, val):
self.dicts[type(key)][key] = val
def __getitem__(self, key):
return self.dicts[type(key)][key]
Then:
>>d = DictByType()
d[1]='foo'
d[True]='bar'
d[1.0] = 'foobar'
d[1], d[True], d[1.0]
('foo', 'bar', 'foobar')

If, as seems to be the case, you have many keys with few types, then the
memory overhead will be smaller. Access time might suffer though.

--
Arnaud
Nov 4 '08 #5

P: n/a
Prateek <su*****@gmail.comwrites:
>b = {0:'xyz', False:'abc'}
b
{0: 'abc'} # Am I the only one who thinks this is weird?
You're not the only one; I consider this a wart in Python.

False and True should be discrete values that don't compare equal with
any other value, not even ones that evaluate to boolean False or True.
>>False == []
False
>>False == {}
False
>>False == ''
False
>>False == 0
True

This is an artifact of the fact that ‘bool’ is a subclass of ‘int’,
and that ‘False’ is implemented such that it has the same hash as ‘0’:
>>isinstance(False, int)
True
>>hash(False) == hash(0)
True

The above is a legacy of the original lack of a boolean type in
Python, but it's confusing and IMO unnecessary these days. It should
be enough that they all evaluate to False in a boolean context:
>>bool([])
False
>>bool({})
False
>>bool('')
False
>>bool(0)
False

PEP 285 <URL:http://www.python.org/dev/peps/pep-0285>, that introduced
the ‘bool’ type, addresses this issue:

In an ideal world, bool might be better implemented as a
separate integer type that knows how to perform mixed-mode
arithmetic. However, inheriting bool from int eases the
implementation enormously (in part since all C code that calls
PyInt_Check() will continue to work -- this returns true for
subclasses of int). Also, I believe this is right in terms of
substitutability: code that requires an int can be fed a bool
and it will behave the same as 0 or 1. Code that requires a
bool may not work when it is given an int; for example, 3 & 4
is 0, but both 3 and 4 are true when considered as truth
values.

I wonder if any of that is relevant any more.

What does backward-compatibility-is-less-important Python 3 do (I
don't have it installed)? Is there a later PEP that I've missed which
finally makes ‘bool’ a type independent from ‘int’?

--
\ “A ‘No’ uttered from deepest conviction is better and greater |
`\ than a ‘Yes’ merely uttered to please, or what is worse, to |
_o__) avoid trouble.” —Mahatma Gandhi |
Ben Finney
Nov 4 '08 #6

P: n/a
"Prateek" <su*****@gmail.comwrote:
>b = {0:'xyz', False:'abc'}
b
{0: 'abc'} # Am I the only one who thinks this is weird?
No. This was discussed before on this list, and that discussion
referenced (if I recall correctly) the discussion on python-dev
at the time the decision was taken.

If my name were Laura Creighton, I would now say:

"I told you so!"

Now I have to weigh my words carefully to make sure I
am not misunderstood:

You are screwed!

- Hendrik

Nov 5 '08 #7

P: n/a
Ben Finney:
Is there a later PEP that I've missed which
finally makes bool a type independent from int?
In a tidy language like an ObjectPascal or Java bools and integers are
different types.

In Python if bools become distinct from integers you have to rewrite
things like:
sum(el == val for el in iterable)
as:
sum(1 for el in iterable if el == val)

In the past here I have stated that boolean operators should return
only boolean values, and I believe it still. Because doing otherwise
is quite confusing.

But in practice I have seen that while being a little untidy, having
bools as subtype of int doesn't lead to much bugs, and it has few
practical advantages. So purity isn't that useful here.

Having an iterable that contains both ints and bools isn't too much
common, because while Python isn't statically typed, in practice most
of the times in most Python programs types are uniform and predictable
(that's why ShedSkin can work).

And even if you have a list that contains bools and integers mixed,
then having to tell them apart (by hashing, etc) is quite uncommon, I
think I have never had to do it in 2-3 years. So maybe is that code
that is doing something messy, so maybe is that code that has to
change and become more tidy, and not the language itself :-)

Bye,
bearophile
Nov 5 '08 #8

P: n/a
Prateek:
How do I make a dictionary which has distinct key-value pairs for 0,
False, 1 and True.
Why do you have to do that? What's the problem you have to solve?
Maybe (probably) there are better or more clean alternative solutions.

Bye,
bearophile
Nov 5 '08 #9

P: n/a
On Wed, 05 Nov 2008 04:22:32 -0800, bearophileHUGS wrote:
In Python if bools become distinct from integers you have to rewrite
things like:
sum(el == val for el in iterable)
as:
sum(1 for el in iterable if el == val)
I would expect that you can still "cast" `bool`\s to `int`\s then.
Sometimes I write it as

sum(int(el == val) for el in iterable)

just for clarity what the intent is.

Ciao,
Marc 'BlackJack' Rintsch
Nov 5 '08 #10

P: n/a
On Nov 4, 3:48*pm, Prateek <sure...@gmail.comwrote:
I've been using Python for a while (4 years) so I feel like a moron
writing this post because I think I should know the answer to this
question:

How do I make a dictionary which has distinct key-value pairs for 0,
False, 1 and True.
As I have learnt, 0 and False both hash to the same value (same for 1
and True).
>b = {0:'xyz', False:'abc'}
b

{0: 'abc'} *# Am I the only one who thinks this is weird?

This obviously stems from the fact that 0 == False but 0 is not False
etc. etc.

That doesn't help my case where I need to distinguish between the two

The same issue applies in a list:

Suppose I do:
>a = [0, 1, True, False]
a.index(False)

0

Wha??? Help.

Let me suggest using surrogate objects instead of True and False:

class TrueObject(object):
def __nonzero__(self):
return True

class FalseObject(object):
def __nonzero__(self):
return False

DistinctTrue = TrueObject()
DistinctFalse = FalseObject()
If that doesn't work and you really need the actual True and False
objects to be distinct, it's not too hard to special case a dict to
get it:

class special_dict(dict):
def __init__(self,*args,**kwargs):
self.true_surrogate = object()
self.false_surrogate = object()
super(special_dict,self).__init__(*args,**kwargs)
def __getitem__(self,value):
if value is True:
value = self.true_surrogate
elif value is False:
value = self.false_surrogate
super(special_dict,self).__getitem__(value)
## etc.
Carl Banks
Nov 5 '08 #11

P: n/a
Prateek <su*****@gmail.comwrites:
How about using (x, type(x)) as the key instead of just x?
Yup. I thought of that. Although it seems kinda unpythonic to do so.
Especially since the dictionary is basically a cache mostly containing
strings. Adding all the memory overhead for the extra tuples seems
like a waste just for those four keys.
You could use a second dict for the other type:

def lookup(x):
if x in dict1: return dict1[x]
return dict2[x]

dict1 would have the 4 special keys and dict2 would have the regular
keys.
Nov 7 '08 #12

P: n/a
On Nov 7, 11:54*am, Paul Rubin <http://phr...@NOSPAM.invalidwrote:
Prateek <sure...@gmail.comwrites:
How about using (x, type(x)) as the key instead of just x?
Yup. I thought of that. Although it seems kinda unpythonic to do so.
Especially since the dictionary is basically a cache mostly containing
strings. Adding all the memory overhead for the extra tuples seems
like a waste just for those four keys.

You could use a second dict for the other type:

* def lookup(x):
* * if x in dict1: return dict1[x]
* * return dict2[x]

dict1 would have the 4 special keys and dict2 would have the regular
keys.

Ummm how do you get "the 4 special keys" in dict1?
E.g.
| >>dict(((0, '0'), (1, '1'), (False, 'f'), (True, 't')))
| {0: 'f', 1: 't'}
| >># Whoops!

Unless I'm missing something, the whole reason for the OP's problem is
that 0/False and 1/True are indistinguishable as dict keys (and set
members).

An alternative solution:
def lookup(x):
if x is False: return FALSE_VALUE
if x is True: return TRUE_VALUE
return normal_dict[x]

Cheers,
John
Nov 7 '08 #13

P: n/a
John Machin <sj******@lexicon.netwrites:
You could use a second dict for the other type:

* def lookup(x):
* * if x in dict1: return dict1[x]
* * return dict2[x]

dict1 would have the 4 special keys and dict2 would have the regular
keys.


Ummm how do you get "the 4 special keys" in dict1?
oops, adjoin the type

def lookup(x):
if (x,type(x)) in dict1: return dict1[x]
return dict2(x)

so dict1 would contain tuples as keys, but just a few of them.
Nov 7 '08 #14

P: n/a
On Nov 7, 3:05*pm, Paul Rubin <http://phr...@NOSPAM.invalidwrote:
John Machin <sjmac...@lexicon.netwrites:
You could use a second dict for the other type:
def lookup(x):
if x in dict1: return dict1[x]
return dict2[x]
dict1 would have the 4 special keys and dict2 would have the regular
keys.
Ummm how do you get "the 4 special keys" in dict1?

oops, adjoin the type

* * *def lookup(x):
* * * *if (x,type(x)) in dict1: return dict1[x]
Possibly you meant:

key = (x, type(x))
if key in dict1: return dict1[key]
* * * *return dict2(x)

so dict1 would contain tuples as keys, but just a few of them.
Nov 7 '08 #15

This discussion thread is closed

Replies have been disabled for this discussion.