bool is instance of int in Python

05 December 2008   12 comments   Python

Powered by Fusion×

I lost about half an hour just moments ago debugging this and pulling out a fair amount of hair. I had some code that looked like this:

result = []
for key, value in data.items():
   if isinstance(value, int):
       result.append(dict(name=key, value=value, type='int'))
   elif isinstance(value, float):
       result.append(dict(name=key, value=value, type='float'))
   elif isinstance(value, bool):
       result.append(dict(name=key, type='bool',
                          value=value and 'true' or 'false'))

It looked so simple but further up the tree I never got any entries with type="bool" even though I knew there were boolean values in the dictionary.

The pitfall I fell into was this:

>>> isinstance(True, bool)
>>> isinstance(False, bool)
>>> isinstance(True, int)
>>> isinstance(False, int)

Not entirely obvious if you ask me. The solution in my case was just to change the order of the if and the elif so that bool is tested first.


Steve Holden
It is indeed kind of warty.

Presumably you already knew about this:

>>> d = {(3.0 +0j): "something"}
>>> d[3]

The Boolean behavior is similar, though slightly different. This was not one of Guido's best ideas - *and* it was introduced in a minor release!
Is this the case in Python 3000 as well? Seems like a bad design decision that might have been changed ;)
It is perfectly logical, if you were around when the bool type was added to python (sometime around 2.2 or 2.3).

Prior to introduction of an actual bool type, 0 and 1 were the official representation for truth value, similar to C89. To avoid unnecessarily breaking non-ideal but working code, the new bool type needed to work just like 0 and 1. This goes beyond merely truth value, but all integral operations. No one would recommend using a boolean result in a numeric context, nor would most people recommend testing equality to determine truth value, no one wanted to find out the hard way just how much existing code is that way. Thus the decision to make True and False masquerade as 1 and 0, respectively. This is merely a historical artifact of the linguistic evolution.

In fact, give this a try:

>>> True == 1
>>> True == 0
>>> False == 0
>>> False == 1
>>> True + 2
>>> False - 5

@thp: Good point. This is something python3 should have corrected while the opportunity presented itself.
Peter Bengtsson
Good point about the backward compatibility issue. It was never going to be easy. I'll let this one slide. However it would have been nice to have that fixed for py3
Interesting, looks like bool is a subtype of int.

>>> bool.mro()
[<type 'bool'>, <type 'int'>, <type 'object'>]
Andrew Veitch
I would use:

if type(value) is bool:
elseif type(value) is int:
elseif type(value) is float:

which will work as expected.
Peter Bengtsson
I like that one! It looks neat and pythonic. Maybe I should stop using isinstance() for basic types and use that weapon more around custom classes and instance objects.
Chris Nasr
This is actually less useful than the original. With this code only bools, integers, and floats would be checked, meaning any subclassing of int or float would not be caught.
result.append(dict(name=key, type=type(value), value=value))
if isinstance(value, bool):
result[-1]['value'] = ("false", "true")[value]
Nitin Sharma
very interesting...thanks for the post :-)
Humairaa Variava
very interesting it helped a lot......thanks for the post:-)
Weizhong Tu
it is easy to get max value in this way, a hack way

max_val = [a, b][a<b]

Your email will never ever be published

Related posts

Finally got rid of the system beep 22 November 2008
Nasty surprise of Django cache 09 December 2008
Related by keywords:
To readline() or readlines() 12 March 2004
Reciprocal lesson about gender perspectives 02 September 2011
Nginx vs. Squid 17 March 2009
IssueTrackerProduct now officially abandoned 30 March 2012
How and why to use django-mongokit (aka. Django to MongoDB) 08 March 2010
On the command line no one can hear you screen. Or can they? 03 May 2012
Nasty surprise of Django cache 09 December 2008
Random ID generator for Zope 02 September 2005
Google Calendar, iCalendar Validator but not bloody Apple iCal 09 April 2009
tempfile in Python standard library 07 February 2006
In Django, how much faster is it to aggregate? 27 October 2010
Google and Python code 22 February 2006