90

Can someone give me an explanation why isinstance() returns True in the following case? I expected False, when writing the code.

print isinstance(True, (float, int))
True

My guess would be that its Python's internal subclassing, as zero and one - whether float or int - both evaluate when used as boolean, but don't know the exact reason.

What would be the most pythonic way to solve such a situation? I could use type() but in most cases this is considered less pythonic.

Georgy
  • 12,464
  • 7
  • 65
  • 73
jake77
  • 1,892
  • 2
  • 15
  • 22

6 Answers6

130

For historic reasons, bool is a subclass of int, so True is an instance of int. (Originally, Python had no bool type, and things that returned truth values returned 1 or 0. When they added bool, True and False had to be drop-in replacements for 1 and 0 as much as possible for backward compatibility, hence the subclassing.)

The correct way to "solve" this depends on exactly what you consider the problem to be.

  • If you want True to stop being an int, well, too bad. That's not going to happen.
  • If you want to detect booleans and handle them differently from other ints, you can do that:

    if isinstance(whatever, bool):
        # special handling
    elif isinstance(whatever, (float, int)):
        # other handling
    
  • If you want to detect objects whose specific class is exactly float or int, rejecting subclasses, you can do that:

    if type(whatever) in (float, int):
        # Do stuff.
    
  • If you want to detect all floats and ints, you're already doing that.
user2357112
  • 260,549
  • 28
  • 431
  • 505
  • 1
    It is the second case. That means one has to take care of the order of the comparisons of these built-in types - understandable given the inheritance but quite unusual for python. – jake77 Jun 17 '16 at 19:08
  • 4
    **Deeper Explanation:** `True` and `False` are singleton instances of the class `int`... `print(True.real)` is `1`, and `print(False.real)` is `0`. `print(True == 1)` is true, and `print(False == 0)` is true. So if you're expecting a parameter to be an int, and the user passes a bool instead, then yeah an `isinstance(param, int)` check will "annoyingly" be true for the value they passed, since a bool is an int... but it's kinda harmless because it will be as if they passed `param = 1` or `param = 0`. So you can still do numeric operations on it, since the boolean internally IS an `int`. – Mitch McMabers Oct 17 '19 at 21:14
  • 5
    **Deeper Explanation Part 2:** You can even do `print(True + 3)` which is `4` (1 + 3), and `print(False + 3)` which is `3` (0 + 3). Hehe. But as noted in the answer above, if you want to be 100% sure they passed an `int` and not a `bool`, you simply check `if type(param) is int`, since that won't accept any subclasses. (Just be careful if your API users might be passing their own custom objects derived from `int` as a baseclass, for example if they make a number class which is an `int` with some extra methods... since their subclass would also be rejected by such a strict check...) – Mitch McMabers Oct 17 '19 at 21:16
9

You can see the method resolution order, and find all superclasses from there:

>>> bool.__mro__
(<class 'bool'>, <class 'int'>, <class 'object'>)
Vicrobot
  • 3,795
  • 1
  • 17
  • 31
8

Yes, this is right, it's a subclass of int, you can verify it using the interpreter:

>>> int.__subclasses__()
[<type 'bool'>]
sbz
  • 893
  • 11
  • 9
5

If you only want to check for int:

if type(some_var) is int:
    return True

else:
    return False
Eyal Levin
  • 16,271
  • 6
  • 66
  • 56
0

See some behaviors (Not so wierd) of python on bool and int

>>> 1 == True  
True           
>>> 0 == False 
True           
>>> True*5 == 0
False          
>>> True*5 == 5
True           
>>> 

How interchangeable can they be used...!

From boolobject.h (win py 2.7) I can see a typedef of int for bool obj. So it is pretty evident that bool has inherited few facial features of int.

#ifndef Py_BOOLOBJECT_H
#define Py_BOOLOBJECT_H
#ifdef __cplusplus
extern "C" {
#endif


typedef PyIntObject PyBoolObject;
Venfah Nazir
  • 320
  • 2
  • 6
0

Here is a instance checker that is safe with bool's and takes single types or tuples of types just like isinstance()

def isInst(o, of) -> bool:
    if o is None: return False
    cls = o.__class__
    if isinstance(of, type):
        return cls == of

    else:
        if cls == bool:
            return bool in of
        else:
            for i in range(len(of)):
                if cls == of[i]: return True

    return False
Timothy C. Quinn
  • 3,739
  • 1
  • 35
  • 47